MENU

Vue 学习笔寄

2022-10-30 • 笔记

基础内容

通过 data()提供数据

vue 中可以在 export default{}里通过 data()提供数据,data必须是一个函数,return一个对象

export default {
  data () {
    return {
      uname: 'JuziYou',
      age: 18
    }
  }  
}

使用插值表达式显示数据

插值表达式使用两对大括号包裹,使用的数据需要在 data()methodscomputed等地方存在,可以使用表达式,但是不能使用在标签属性

{{ uname }}
{{ obj.uname }}
{{ obj.age > 18 ? '成年' : '未成年' }}

v-bind

插值表达式不能使用在标签属性,需要使用 v-bind需要设置动态 html 属性上,简写为 :

举例:

<a :href="url">戳戳</a>
<a v-bind:href="url">戳戳</a>


v-on

v-on语法为 v-on:事件名='需要执行的代码/函数名/函数名(实参)'需要在 methods中提供事件处理函数,简写 @

举例:

<button @click='num = bum + 1'>Click</button>
<button @click='functionA'>Click</button>
<button @click='functionB('JuziYou')'>Click</button>
<button v-on:click='num = bum + 1'>Click</button>
<button v-on:click='functionA'>Click</button>
<button v-on:click='functionB('JuziYou')'>Click</button>

事件修饰符

.prevent阻止默认行为
.stop阻止冒泡
.once程序运行期间只触发一次事件处理函数
.native在组件上调用事件


v-model

v-model是一个语法糖 @input:value组合到了一起,它吧属性和数据双向绑定到一起。双向绑定即数据变化视图同步变化,视图变化数据同步变化。
Vue3 中 v-model:modelValue@update:modelValue 的组合,Vue3官方文档

语法:v-model="数据变量"

<button v-model="数据变量"> 戳戳 </button>

在父子组件通信时,如果 :value传参给子组件 $emit('input', 值)可以使用 v-model代替

v-model 修饰符

.number.parseFloat转换为数字类型
.trim去除首尾空白(空格)
.lazy使用 change事件改变数据(默认为 imput事件改变数据)


v-textv-html

v-textv-html会更新 DOM 对象的 innerText 或者 innerHTML,并且会覆盖差值表达式 v-text不会识别 HTML 标签,v-html会识别 HTML 标签

语法:

<p v-text="数据变量"></p>
<p v-html="数据变量"></p>

v-showv-ifv-else

v-showv-if控制标签的显示或隐藏,truefalse
v-show使用 CSS 的 display:none来隐藏
v-if将会直接从 DOM 树上移除
频繁切换显示的情况下使用 v-show因为使用 display:none来切换显示隐藏不会频繁创建元素节省性能,v-if是惰性的在 false时不会创建元素节省初期渲染开销

语法:

<p v-show="true"></p>
<p v-if="false"></p>

v-ifv-elsev-else-if同时使用,方便通过变量控制一套标签出现或者隐藏

<template>
  <div>
    <h1 v-if="yukari>= 60">紫老ffhfhewufuiefefrwgfuktyr</h1>
    <h1 v-else-if="yukari>= 30">紫妈</h1>
    <h1 v-else>紫</h1>
  </div>
</template>

<script>
export default {
  data(){
    return {
      yukari: 16
    }
  }
}
</script>

v-for

v-for 常用于列表渲染,按照数据循环生成,可以遍历数组、对象、数字、可遍历解构的字符串
每一项唯一标识符作为 :key="索引",可以最大限度的复用你的 DOM,:key="索引"的索引只能是数字或者字符串

语法:
v-for="(值, 索引) in 目标" :key="索引"
v-for="值 in 目标" :key="值下的可用于索引的值"

<template>
  <div>
    <ul>
      <li v-for="(item,index) in list" :key="index">
        {{ item }} > {{ index }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data(){
    return {
      list:['JuziYou','MikanpaperYuzu','橘纸柚']
    }
  }
}
</script>
<template>
  <div>
    <ul>
      <li v-for="(value, key) in preson" :key="key">
        {{ value }} · {{ key }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data(){
    return {
      preson:{
        uname: '八云紫',
        skill: '操纵界线的能力',
        age: 18
      }
    }
  }
}
</script>
<template>

<div>
    <ul>
      <li v-for="(item,index) in personList" :key="index">
        >>>{{item.uname}}
        <p>{{item.skill}}</p>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data(){
    return {
      personList:[
        {uname:'博丽灵梦',home:'博丽神社'},
        {uname:'琪露诺',home:'雾之胡'},
        {uname:'蕾米莉亚',home:'红魔馆'},
        {uname:'帕秋莉',home:'红魔馆'}
      ]
    }
  }
}
</script>
<template>
  <div>
    <ul>
      <li v-for="(item,i) in 5" :key="i">
        {{item}} · {{i}}
      </li>
    </ul>
  </div>
</template>


数组的数据更新监测

数组方法中能引起页面更新的方法有:
pushpopshiftunshiftsplicesortreverse
其他无法引起页面更新的方法可以通过赋值来完成更新
通过下标来更新值不会触发页面更新需要使用 $set来更新数组
语法:this.$ste(要改变的对象, 要改变的位置, 要改变的值)

<template>
  <div>
    <ul>
      <li v-for="(item,index) in list" :key="index">{{item}}</li>
    </ul>
    <button @click="changeFn">changeFn</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: ['博丽灵梦', '琪露诺', '八云紫']
    }
  },
  methods: 
    changeFn(){
      this.$set(this.list, 1, "魔理沙")
    }
  }
}
</script>

动态绑定 class

使用 v-bind:class设置动态属性一般使用简写 :class允许使用对象或者数组
:class不会影响到原有的 class 属性
使用对象动态绑定 :class="{类名:布尔值}"如果布尔值为 true就有这个类名,false则没有
使用数组动态绑定 :class="数组"标签会有数组里面的所有类名

<template>
  <div>
    <div :class="str">Nya</div>
  </div>
</template>

<script>
export default {
  data(){
    return {
      str: 'green'
    }
  }
}
</script>
<template>
  <div>
    <div :class="isRed ? 'red' : ''"></div>
  </div>
</template>

<script>
export default {
  data(){
    return {
      isRed: true
    }
  }
}
</script>
<template>
  <div>
    <div :class="{yukari: isRed, suka: !isRed}"></div>
  </div>
</template>

<script>
export default {
  data(){
    return {
      isRed: true
    }
  }
}
</script>
<template>
  <div>
    <div :class="arr"></div>
  </div>
</template>

<script>
export default {
  data(){
    return {
      arr: ['show', 'mask', 'red', 'paper']
    }
  }
}
</script>


动态绑定 style

使用 v-bind:style设置动态属性一般使用简写 :style
语法 :style="{样式名:样式值}"样式名为 CSS 属性的驼峰命名法

<template>
  <div>
    <h1 :style="{ fontSize: '50px', color: fontColor}">黄色的</h1>
  </div>
</template>

<script>
export default {
  data(){
    return{
      fontColor: 'yellow'
    }
  }
}
</script>

computed计算属性

计算属性必须在 computed节点中,多用于多个数据影响一个数据的场景,根据现有的数据区计算出新的数据并且有缓存,在数据不发生变化的情况下只会执行一次,数据有变化时会重新计算并缓存,写法是一个函数,一定需要返回值是最终值

示例翻转字符串:

<template>
  <div>
    <h1>{{reverseStr}}</h1>
  </div>
</template>

<script>
export default {
  data(){
    return{
      uname: 'MikanpaperYuzu'
    }
  },
  computed: {
    reverseStr(){
      return this.uname.split('').reverse().join('')
    }
  }
}
</script>

计算属性默认情况下只能获取不能修改,如果需要修改需要用到完整写法

完整写法示例:

<template>
  <div>
    <h1>计算属性的完整写法</h1>
    <h1>{{fullName}}</h1>
    <button @click="clickFn">点我</button>
  </div>
</template>

<script>
export default {
  data(){
    return{
      firstName:'Juzi',
      lastName:'You'
    }
  },
  computed: {
    fullName: {
      get(){
        return this.firstName + '-' + this.lastName
      },
      set(val){
        this.firstName = val.split('-')[0]
        this.lastName = val.split('-')[1]
      }
    }
  },
  methods: {
    clickFn(){
      this.fullName = 'MikanPaper-Yuzu'
    }
  }
}
</script>

watch属性监听

属性监听必须写在 watch节点中,只要监听的属性值发生了变化就会执行,多用于一个属性影响多个属性的场景,没有缓存但是可以异步,简单写法无法监听复杂类型的写法,需要使用完整写法监听复杂数据类型

watch: {
  监听的属性 (变化后的值, 变化前的值) {
  }
}

示例:

<template>
  <div>
    <h2>{{ uname }}</h2>
    <button @click="uname = 'MikanpaperYuzu'">给我变</button>
  </div>
</template>

<script>
export default {
  data(){
    return{
      uname: 'JuziYou'
    }
  },
  watch:{
    uname(newVal, oldVal){
      console.log('魔法少女 ' + oldVal + ' 变 ' + newVal);
    }
  }
}
</script>
动态属性是一个对象的形式
immediate: true代表一进入页面立即执行
deep: true代表升读监听可以监听到复杂数据类型内部的变化
handler(){}每次变化时触发的函数

watch: {
  接听的属性:{
    immediate: true, //代表一进入页面立即执行
    deep: true,      // 代表升读监听可以监听到复杂数据类型内部的变化
    handler(){       // handler 是每次变化时触发的函数
    }
  }
}

示例:

<template>
  <div>
    <h2>{{ obj.uname }}</h2>
    <button @click="mya">给我变</button>
  </div>
</template>

<script>
export default {
  data(){
    return{
      obj: {
        uname : 'JuziYou'
      }
    }
  },
  watch: {
    obj:{
      immediate: true,
      deep: true,
      handler(){
        console.log("变了喵");
      }
    }
  },
  methods: {
    mya(){
      this.obj.uname = "MikanpaperYuzu"
    }
  }
}
</script>


组件基础

引入组件

组件分为全局引入和局部引入,一般使用大驼峰命名法,除了驼峰, 如果使用小写还可以使用 -转换链接不同的单词,如一个注册组件名为 <JuziYou />可以直接使用转换连接 <juzi-you />也能解析到父组件。如果标签没有内容可以直接使用自结束标签

局部引入

局部引入将组件注册在当前 Vue 文件,只可以用在这个 Vue 文件中。
使用 import 组件对象 from 'Vue文件路径'引入组件
然后 components: { 组件名: 组件对象 }注册组件
示例:

<template>
  <div id="app">
    <Pannel />
  </div>
</template>

<script>
import PannelLoc from './components/Pannel.vue'
export default {
  components: { Pannel: PannelLoc }
}
</script>
<template>
  <div>
    <h1>Nya</h1>
  </div>
</template>

全局引入

全局引入在 main.js引入组件并使用,注册在全局整个项目中都可以使用
使用 import 组件对象 from 'Vue文件路径'引入组件
然后 Vue.component("组件名", 组件对象)注册组件

示例:

import Vue from 'vue'
import App from './App.vue'

// 引入组件
import Pannel from "./components/Pannel";
// 全局注册
Vue.component('PannelGol', Pannel)

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
<template>
  <div>
    <h1>Nya</h1>
  </div>
</template>
<template>
  <div>
    <PannelGol></PannelGol>
  </div>
</template>


组件样式 scoped

吧样式只应用于当前组件,给组件的 <style>加上 scoped会给这个组件的所有元素添加自定义属性,以及当前组件的 CSS 添加交集选择器,只有这个自定义属性的标签才能被样式选中

示例:

<template>
  <div>
    <h1>MikanpaperYuzu</h1>
    <ChildOne />
  </div>
</template>

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

<style>

</style>
<template>
  <div>
    <h1>JuziYou</h1>
  </div>
</template>

<script>
export default {

}
</script>
<style scoped>
h1{
  color: orange
}
</style>


组件 name属性

一般使用一些框架会用到,以保证框架的命名风格所以懒得写太清楚(((
示例:

<template>
  <div>
    <JuziYou />
  </div>
</template>

<script>
import ChildCom from './components/ChildCom.vue'
export default {
  components:{
    [ChildCom.name]: ChildCom
  }
}
</script>
<template>
  <div>
    <p>JuziYou</p>
  </div>
</template>

<script>
export default {
  name:'JuziYou'
}
</script>


父向子通讯

在父组件的子组件标签上添加自定义属性,然后在子组件使用 props接收
注: 子组件不能修改父组件传来的 props

示例:

<template>
  <div>
    <MyProduct title="勾勾果" price="200" intro="蒙德产勾勾果" />
    <MyProduct title="甜甜花" price="100" intro="蒙德产甜甜花" />
  </div>
</template>

<script>
// 定义局部变量
import MyProduct from './components/MyProduct.vue'
export default {
  components: {
    MyProduct,
  }
}
</script>
<template>
  <div>
    <h3>标题: {{ title }}</h3>
    <p>价格: {{ price }}元</p>
    <p>{{ intro }}</p>
  </div>
</template>

<script>
export default
  props: ['title','price','intro']
}
</script>


子向父通讯

在父组件的标签上设置自定义事件组件 @事件名="方法名",然后在子组件中使用 this.$emit(事件名, 传给父元素的值)触发自定义事件,$emit第二个值之后为返回给父元素的值,在父亲元素接收的方法中对应形参

示例:

<template>
  <div>
    <MyProduct
        :title="list.title"
        :price="list.price"
        :intro="list.intro"
        @upMora="upFn"
    />
  </div>
</template>

<script>
// 定义局部变量
import MyProduct from './components/MyProduct.vue'
export default {
  components: {
    MyProduct,
  }
  data() {
    return {
      list:{
        title: '勾勾果'
        price: '200'
        intro: '蒙德产勾勾果'
      }
    }
  },
  methods: {
    upFn(num){
      this.list.price = num
    }
  }
}
</script>
<template>
  <div>
    <h3>标题: {{ title }}</h3>
    <p>价格: {{ price }}元</p>
    <p>{{ intro }}</p>
    <button @click="$emit('upMora', 1000)">价格改为1000元</button>
  </div>
</template>

<script>
export default
  props: ['title','price','intro']
}
</script>

props校验

校验父模块向子模块传来的数据是否符合需求

key是接收值名称,value是接收的类型

export default {
    props:{
        user:String
    }
}
如果需要多个类型,key 是接收者名称,value设置为接收的类型数组

export default {
    props:{
        age: [String, Number]
    }
}
如果设置为必传参数将使用完整写法写为对象的形式,type表示接收的类型,required: true来打开必传

export default {
    props:{
        gender:{
          type: String,
          required: true
        }
    }
}
如果设置默认值将使用完整写法写为对象的形式,type表示接收的类型,default: 值来设置默认值

export default {
    props:{
        salary:{
          type: String,
          default: 'JuziYou'
        }
    }
}
如果设置自定义校验规则将使用完整写法写为对象的形式,type表示接收的类型,使用 validator(传过来的值){}return出来的值 truefalse来校验通过或不通过,如果 returnfalse将会在控制台打印错误

export default {
    props:{
        // 检测传入 list 是否 length 大于 3
        list: {
          type: Array,
          validator(val){
            if(val.length >= 3){
              return true
            }
            return false
          }
        }
    }
}

.sync修饰符

可以让繁琐的父子通讯变得简单在使用.sync修饰符后在子组价使用 update:自定义变量 来更改父组件变量绑定的值就可以完成父子组件的通讯,相当于在父组件的标签上添加了@update:属性名=" 变量名 = $event"$event 是默认参数

<template>
  <div>
    <SyncUpdate :text.sync="name"/>
  </div>
</template>

<script>
import SyncUpdate from '@/components/sync-update.vue'
export default {
  components:{
    SyncUpdate
  },
  data () {
    return {
      name: 'Mikanpapre'
    }
  }
}
</script>
<template>
  <div>
    <div>{{ text }}</div>
    <!-- 使用 update:自定义变量 来更改父组件变量绑定的值 -->
    <button @click="$emit('update:text', 'Nya')">戳戳</button>
  </div>
</template>

<script>
export default {
  props: ['text']
}
</script>

$parent

$parent可以在子组件获取到父组件的实例


事件总线 EventBus

如果两个子组件通过父组件进行通讯会非常复杂,通过一个空的 Vue 对象当作事件总线常用于跨组件通讯的通用方案,在组件 A 使用 EventBus.$emit('事件名', 值)触发事件,在组件 B 使用 EventBus.$on('事件名', 函数体)监听事件

使用 import Vue from 'vue'引入 Vue
使用 export default new Vue()对外暴露一个 Vue 实例

import Vue from 'vue'
export default new Vue()
组件 A 触发事件

<template>
  <div>我是A
  <button @click='clickfn'>戳</button>
  </div>
</template>

<script>
import EventBus from '../EventBus'
export default {
  methods:{
    clickfn(){
      EventBus.$emit('mikan')
    }
  } 
}
</script>
组件 B 监听事件,created()在事件创建时就执行

<template>
  <div>我是B</div>
</template>

<script>
import EventBus from '../EventBus'
export default {
    created(){
      EventBus.$on('mikan', ()=>{
        console.log('喵');
      })
    }
  
}
</script>
APP.vue导入两个组件并使用

<template>
  <div>
    <h1>EventBus</h1>
    <ChildA/>
    <ChildB/>
  </div>
</template>

<script>
import ChildA from './components/ChildA.vue'
import ChildB from './components/ChildB.vue'
export default {
  components: {
    ChildA,
    ChildB
  }
}
</script>


ref$refs

ref$refs通常用于获取元素 DOM 或组件示例,将会挂载到 $refs的组件对象内

获取 DOM

在需要获取的 DOM 元素上 ref="值"然后在 $refs.值使用

<template>
  <div>
    <div ref="juziyou">JuziYou</div>
    <button @click="clickFn">Click</button>
  </div>
</template>

<script>
export default {
  methods:{
    clickFn(){
      console.log(this.$refs.juziyou)
    }
  }
}
</script>

获取组件实例

在需要获取的组件自定义标签上 ref="值"然后在 $refs.值使用

<template>
  <div>
    <ChildB ref="childRef"/>
    <button @click="childRefFn">Click</button>
  </div>
</template>

<script>
import ChildB from './components/ChildB.vue'
export default {
  components:{
    ChildB
  },
  methods:{
    childRefFn(){
      console.log(this.$refs.childRef.uname)
      this.$refs.childRef.logName()
    }
  }
}
</script>
<template>
  <div>
    ChildB
  </div>
</template>

<script>
export default {
  data(){
      return{
          uname: 'JuziYou',
      }
  },
  methods:{
      logName(){
          console.log(this.uname)
      }
  }
}
</script>


$nextTick

由于 Vue 更新 DOM 是异步的,会导致需要在 DOM 更新之后的代码会被先执行,所以需要使用 $nextTick
$nextTick接收一个函数作为参数,接收的函数将会在 DOM 更新后执行

如在点击按钮后显示输入框然后获取输入框的 DOM:

<template>
  <div>
    <input type="text" ref="ipt" v-if="isShowInput">
    <button @click="fn" v-else>Click</button>
  </div>
 </template>

<script>
export default {
  data () {
    return {
      isShowInput: false
    }
  },
  methods: {
    fn () {
      this.isShowInput = true
      this.$nextTick(()=>{
        console.log(this.$refs.ipt);
      })
    }
  }
}
</script>

component动态渲染组件

component是 Vue 内置的一个组件用于动态的渲染组件,:is="值"的值是什么就会渲染哪个子组件

<template>
  <div>
    <button @click="comName = 'ChildA'">ChildA</button>
    <button @click="comName = 'ChildB'">ChildB</button>
    <component :is="comName"></component>
  </div>
</template>

<script>
import ChildA from './components/ChildA.vue'
import ChildB from './components/ChildB.vue'
export default {
  components:{
    ChildA,
    ChildB
  },
  data(){
    return{
      comName: 'ChildA'
    }
  }
}
</script>
<template>
  <div>
    <h1>ChildA</h1>
  </div>
</template>
<template>
  <div>
    <h1>ChildB</h1>
  </div>
</template>


自定义指令

自定义指令分为全局和局部注册,在标签上 v-自定义指令名进行使用

常用的:
inserted方法绑定此指令的元素插入到 DOM 时执行
语法为 inserted(形参){}
inserted(形参){}第一个形参是绑定此指令的元素,第二个形参为当前自定义指令传入的值

update方法绑定此指令的元素值发生变化时执行
语法为 update(形参){}
update(形参){}第一个形参是绑定此指令的元素,第二个形参为当前自定义指令传入的值

main.js内添加 Vue.directive进行全局注册
语法为 Vue.directive('指令名',{方法})

示例:在 input 标签创建时自动落焦

Vue.directive('focusglo', {
  inserted(el){
    el.focus()
  }
})
在组件的 export default内使用 directives{对象}进行局部注册,注册的自定义指令只能在当前组件使用

示例:如 color的值发生变化时重新设置使用了此自定义指令的元素的颜色

export default {
  data () {
    return {
      color: 'orange'
    }
  }
  directives: {
    zy:{
      inserted(el,binding){
        el.style.color = binding.value
      },
      update(el,binding){
        el.style.color = binding.value
      }
    }
  }
}


插槽

插槽插在父组件的两个起始标签和结束标签之间,默认会放到默认 <slot>插槽内
<template>中使用指令 v-slot:插槽名来使用指定的插槽,也可以用 #插槽名来简写,在子组件内的 <slot>通过 name="插槽名"变量的方式来设置插槽名
默认插槽可以使用 <template v-slot="变量名"></template >"来接收传过来的参数,具名插槽可以之间加上等号变量名来接收传过来的的参数 <template v-slot:插槽名="变量名"></template >",在子组件内的 <slot>通过添加自定义变量名的方式来传参

<template>
  <div>
    <!-- 使用組件時在父組件的兩個標簽之間插入 -->
    <MyDialog>
      <img src="https://lovemen.cc/usr/hotlink-ok/avatar.jpg" alt="">
    </MyDialog>
    <MyDialog>
      <!-- <template v-slot:footer> 指定插入footer插槽 v-slot:插槽name -->
      <!-- 在具名插槽下直接加上等號就可以接收,可以在等號内直接解構賦值 -->
      <template v-slot:footer="{yes:okay,no}">
        <button>{{ okay }}</button>
        <button>{{ no }}</button>
      </template>
        <!-- 可以使用#簡寫 -->
      <template #header>
        <h3>溫馨提示</h3>
      </template>
      <!-- 默認插槽透過v-slot="變量名"接收傳過來的,將以對象形式呈現,只能在template内使用 -->
      <template v-slot="obj">
        {{obj}}
        <p>你好,買?</p>
      </template>
    </MyDialog>
  </div>
</template>

<script>
import MyDialog from './components/MyDialog.vue'
export default {
  components:{
    MyDialog
  }
}
</script>
<template>
  <div class="my-dialog">
    <div class="header">
      <slot name="header"></slot>
    </div>
    <div class="content">
      <!-- slot表示此處有一個插槽 -->
      <!-- 作用域插槽:通過插槽設置自定義參數傳數據 -->
      <slot uname="MikanpapreYuzu" age="18"></slot>
    </div>
    <div class="footer">
      <!-- name給插槽起名 -->
      <slot name="footer" yes="好捏" no="打咩"></slot>
    </div>
  </div>
</template>

<script>
export default {

}
</script>

<style lang="less" scoped>
.my-dialog {
  width: 400px;
  padding: 10px 20px;
  border: 3px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>


路由

npm 默认获取的 vue-router版本为 4,在 Vue2 中需要 vue-router版本为 3 时才可以使用,所以在 Vue2 中需要 @3指定 vue-router安装版本

npm i vue-router@3

路由就是路径和组件间的关系,创建路由的方式有在 main.js创建路由和创建 router文件夹在 index.js中创建路由

main.js创建路由

先在 main.js内使用 import VueRouter from 'vue-router'引入 vue-router,然后使用 Vue.use(VueRouter)让 Vue 使用 vue-router插件 const router = new VueRouter(对象)创建一个路由实例,在对象内添加一个 routes数组,这个数组内每个对象都是一个匹配规则,import RouterPage from '@/views/RouterPage'引入组件,router 内的对象 path为设置路由的路径 component为设置路由指向的组件,最后在 new Vue内添加 const 出来的变量名关联到 Vue 实例,匹配到的组件会在 <router-view></router-view>渲染

import Vue from 'vue'
import App from './App.vue'
import My from '@/views/My'
import Part from '@/views/Part'

// 5 引入组件
import RouterPage from '@/views/RouterPage'
// 1引入vue-router
import VueRouter from 'vue-router'
// 2让Vue使用vue-router插件
Vue.use(VueRouter)
// 3创建路由实例
const router = new VueRouter({
  // 6 routes 是一个数组,此数组里面的每一个对象都是一个匹配规则
  routes:[
    {
      // 7设置路由路径
      path:'/RouterPage',
      // 8 给路径指向组件
      component: RouterPage
    }
  ]
})

Vue.config.productionTip = false

//4 吧路由实例关联router到vue实例
new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
<template>
  <div>
    <h1>RouterPage</h1>
  </div>
</template>
<template>
  <div>
    <a href="#/RouterPage"></a>
    <div>
      <router-view></router-view>
    </div>
  </div>
</template>

router文件夹中的 index.js创建路由

main.js中创建路由不太好,应该每个模块干每个模块的事情
src中创建 router文件夹然后在文件夹中创建 index.js,首先在 index.js中使用 import Vue from 'vue'引入 Vue,然后使用 import VueRouter from 'vue-router'引入 vue-router,接着使用 Vue.use(VueRouter)让 Vue 使用 vue-router插件,使用 const router = new VueRouter(对象)创建一个路由实例,在对象内添加一个 routes数组,这个数组内每个对象都是一个匹配规则,import RouterPage from '@/views/RouterPage'引入组件,router 内的对象 path为设置路由的路径 component为设置路由指向的组件,export default routerindex.js对外暴露 router实例,最后在 main.js中使用 import router from '@/router/index.js'引入路由实例再在 new Vue中关联引入的实例 router到 Vue 实例,匹配到的组件会在 <router-view></router-view>渲染

// 1 引入Vue
import Vue from 'vue'
// 5 引入组件
import RouterPage from '@/views/RouterPage'
// 2 引入vue-router
import VueRouter from 'vue-router'
// 3 让Vue使用vue-router插件
Vue.use(VueRouter)
// 4 创建路由实例
const router = new VueRouter({
  // 6 routes 是一个数组,此数组里面的每一个对象都是一个匹配规则
  routes:[
    {
      // 7设置路由路径
      path:'/RouterPage',
      // 8 给路径指向组件
      component: RouterPage,
    }
  ]
})

// 对外暴露路由实例对象
export default router
import Vue from 'vue'
import App from './App.vue'

// 引入路由实例
import router from '@/router/index.js'

Vue.config.productionTip = false

//4 吧路由实例关联router到vue实例
new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
<template>
  <div>
    <h1>RouterPage</h1>
  </div>
</template>
<template>
  <div>
    <a href="#/RouterPage"></a>
    <div>
      <router-view></router-view>
    </div>
  </div>
</template>

查询字符串传参

to="path"的 path 后面加 ?查询字符串的方式传参,然后在组件内使用 $route.query接收参数会以一个对象的形式接收

比如 <router-link to="/part?uname=JuziYou&ocname=MikanpaperYuzi">橘纸柚</router-link>,在组件内 $route.query将会收到一个对象 {uname:'JuziYou', ocname'MikanpaperYuzi'}

动态路由传参

首先在对应的路径复制一份然后在 path 中的路由加上 /:参数名,然后在组件内使用 $route.params接收参数会以一个对象的形式接收

比如:
一个路由为 /user需要接收一个参数名为 uname(.....省略后面或者前面代码)

..... = new VueRouter({
  routes:[
  .....
  .....
    {
      path:'/user',
      redirect: 'User' 
    },
    {
      path:'/user/:uname',
      redirect: 'User' 
    },
  .....

然后访问比如 localhost:8080/#/user/juziyou,在组件内使用 $route.params将会获取到一个对象 {uname:'juziyou'}

路由重定向以及默认页(404)

在路由实例的 routes中起始位子添加一个匹配规则,匹配规则中 redirect为重定向的路径

如从 /重定向到 /juziyou(.....省略后面或者前面代码)

..... = new VueRouter({
  routes:[
    {
      path:'/',
      redirect: '/juziyou' 
    },
  .....

默认页则在路由实例的 routes中的末尾添加一个 path为通配符 *的一个规则

如前面一个规则也没匹配上返回一个 NotFund页面(.....省略后面或者前面代码)

..... = new VueRouter({
  routes:[
  .....
  .....
    {
      path:'*',
      redirect: 'NotFund' 
    },
  .....

mode 与 base

mode写在路由的实例中有个常用的参数 hashhistoryhash会在 URL 中添加#号,history则不会但是需要后端配合因为会向服务器发送真实路径。
以及可以通过 base 设置基础地址,比如要访问 /user 页面在 hash 模式下是 /#/user 如果在 history 模式下设置了 base/juzi/ 地址就是 /juzi/user

如修改 modehistory(.....省略后面或者前面代码)

..... = new VueRouter({
  mode: 'history',
  base: '/juzi/'
  .....

router-link声明式导航

声明式导航 router-linkvue-router提供的一个全局组件,使用 to="path"来跳转路由路径
会给选中的路由自动的加上 class 类名,其分为模糊匹配类名和精确匹配类名,可以在路由实例内通过 linkActiveClas修改模糊匹配类名以及通过 linkExactActiveClass修改精确匹配类名

如通过 router-link转到 path /routerpage

<router-link to="/routerpage"></router-link>

如需要修改精确匹配类名为:juzi,模糊匹配类名为:you 在路由实例内设置的方式(.....省略后面或者前面代码)

...... = new VueRouter({
  linkActiveClass:'you',
  linkExactActiveClass:'juzi',
  ......

编程式传参

通过调用函数的方式使用方法 $router.push,使用 path无法使用 params传参所以推荐使用 name来进行跳转

name

在路由配置中添加 name属性,在编程式导航中如果需要使用 params就得用 name来跳转

路由守卫

路由守卫分为全局路由守卫、路由内独享守卫、组件内守卫

全局路由守卫:对所有路由守卫生效

  • beforeEach 路由前置守卫:token 判断
  • arterEach 路由后置守卫:进度条关闭

路由独享守卫:对某一条路由进行单向控制

  • beforeEnter 路由独享守卫:写在路由配置项内,也有 to from next 只对某条路由规则生效

组件内守卫:针对组件内

  • beforeRouterEnter 只要通过路由就会触发
  • beforeRouteUpdate: 再次复用了页面,比如主页跳到带 params 的主页,解决 created 只会触发一次的问题
  • beforeRouterLeave: 代表离开了页面

全局守卫

所有的路由一旦匹配到规则在真正渲染解析前都会经过全局守卫,只有在全局守卫放行后才能真正渲染页面
在创建实例前先声明一个白名单变量或常量,吧路由(path)以字符串的形式储存在数组中,然后使用router.beforeEach(回调函数)创建路由守卫,在路由解析访问之前都会经过回调函数,回调函数中有三个形参from从哪里来的路由信息对象、to要到那里去的路由信息对象、next是否放行这是一个方法,如果next()表示放行去的页面、next(路径)则表示拦截到的页面,使用whiteList.includes来匹配是否处于白名单

如验证用户是否登录

// 设置白名单
const whiteList = ['/login', '/regisrer']
router.beforeEach((to, from, next) => {
  // 如果有token就放行
  if (this.token) {
    next()
  } else {
    // 如果没有token检查是不是去白名单页面
    if (whiteList.includes(to.path)) {
      // 如果是去白名单直接放行
      next()
    } else {
      // 如果不是去白名单跳转到登录
      next('/login')
    }
  }
})

生命周期

生命周期是 Vue 组件创建到销毁的过程
在特定的时间点执行特定的操作
分四大阶段,八个方法
官方文档

初始化

beforeCreate在组件创建之前会被调用,此状态下无法获取自定义的数据,比如 data中的数据
created会在初始化数据并当前实例代理了数据后执行,一般会在此生命周期钩子中请求服务器数据

挂载

beforeMount无法获取到 DOM 元素因为这个时候还是虚拟 DOM
mounted中就可以获取到真实 DOM 了

更新

beforeUpdate在页面使用中的数据发生变时虚拟 DOM 更新之前运行
updated在使用中的数据发生变化时虚拟 DOM 更新到页面后运行

销毁

beforeDestroy在销毁前运行,一般用来解绑一些事件,比如说定时器
destroyed咋子销毁后运行

keep-alive缓存

<keep-alive>是 Vue 内置的全局组件可以吧组件缓存到内存中,使用<keep-alive>被缓存的组件有两个生命周期activated组件被激活deactivated组件被隐藏


vuex

vuex 是 vue 的一个状态(数据)管理工具(也是插件),方便的解决多组件的共享状态,拥有响应式、操作简洁的优势
一般情况下多组件的数据共享会存在 vuex 中

创建仓库

一般为了维护项目的目录整洁会在src目录下新建store目录放置index.js
首先在创建 Vuex 实例然后再在main.js中导入挂载到 Vue 实例上
Vuex.Store()括号内是一个对象

// 引入Vue
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 注册vuex插件
Vue.use(Vuex)
// 创建仓库实例
const store = new Vuex.Store()
// 导出仓库
export default store
import Vue from 'vue'
import App from './App.vue'
// 引入store
import store from '@/store'

Vue.config.productionTip = false

new Vue({
  // 挂载到 Vue 实例
  store,
  render: h => h(App)
}).$mount('#app')

state

state 提供唯一的公共数据源,所有的共享数据需要放到 state 中储存,可以在任何组件中使用$store.state.键来获取 state 中的数据也可以通过mapState()映射到computed

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
  // 设置 State
  state:{
    uname: 'JuziYou',
    age: 18
  }
})
export default store
<template>
  <div>
    <!-- 直接使用 $store.state.uname 获取 uname 的值 -->
    <p>{{ $store.state.uname }}</p>
    <!-- 直接使用映射计算属性得到的值 -->
    <p>{{ age }}</p>
  </div>
</template>

<script>
// 引入 mapState 
import { mapState } from 'vuex'
export default {
  computed:{
    // mapState() 得到的是一个数组中对应的对象可以使用 ... 展开运算符展开到计算属性中
    ...mapState(['age'])
  }
}
</script>

mutation

state 中的数据只能通过 mutation 修改,用于父组件需要修改仓库中的数据
在 vuex 实例中定义mutation,它是一个对象里面存储修改 state 的方法,每个方法的第一个形参是当前仓库的 state,第二个是传入的参数
可以通过$store.commit('方法', 传参)来调用 mutation 中的方法,传参只能传一个参数如果需要传多个使用复杂数据类型来完成,比如说对象
还可以通过辅助函数mapMutations(数组)的方式使用展开运算符映射到methods中,需要使用this.方法来调用,使用起来和mapState()很像

actions

actions 负责仓库内的异步操作,mutation 只能用于同步更新数据
在 vuex 实例中定义actions,它是一个对象里面存储异步的方法,每个方法的第一个形参拥有仓库实例的所有属性和方法,第二个是传入的参数传参只能传一个参数如果需要传多个使用复杂数据类型来完成,比如说对象
可以通过$store.dispatch('方法', 传参)来调用
也可以通过辅助函数mapActions(数组)来调用使用展开运算符映射到methods

getters

getters 使用仓库 state 中现有的数据计算出新的数据
在 vuex 实例中定义getters,每个方法的第一个形参是当前仓库的 state,必须要有返回值
可以通过$store.getters.方法来调用
也可以通过辅助函数mapGetters(数组)的方式使用展开运算符映射到computed

module 模块

如果使用单一的index.js项目大后应用会变得非常复杂相对臃肿
store文件夹中创建一个用于存放模块的文件夹,export default向外暴露键statemutationsactionsgetters值就是它们所需要的值,访问模块中的数据可以直接通过$store.state.模块名.数据键来访问mutationsactionsgetters默认情况下全局可以调用,然后在index.js中引入模块并在modules中注册,组件的state以对象的形式挂载到state` 下

export default {
  state: {},
  mutations: {},
  actions: {},
  getters: {}
}
import Vue from 'vue'
import Vuex from 'vuex'
// 引入子模块
import data from '@/store/data'
Vue.use(Vuex)
const store = new Vuex.Store({
  // 使用modules来引入子模块
  // 会将挂载组件的state以对象的形式挂载到state下
  modules:{
    data
  },
})

namespaced 命名空间

开启命名空间后子模块的 mutations、actions、getters 就不会注册在仓库全局,在子模块的export default中添加namespaced: true,开启后$store访问模块内的方法需要模块名/函数名
辅助函数需要在数组前增加模块名如mapState(模块名, 数组),然后通过this.方法来调用
辅助函数还可以通过加/的方式来使用(如mapState(['模块名/方法名']),在调用方法时得this[模块名/方法名]()来调用

export default {
  namespaced: true,
}

createNamespacedHelpers

createNamespacedHelpers辅助函数会返回一个对象里有mapActionsmapGettersmapMutationsmapState通过解构赋值和重命名的方式来提取映射

export default {
  namespaced: true,
  state: {
    uname: 'JuziYou'
  },
  mutations: {
    updateName (state, payload) {
      state.uname = payload
    }
  }
}
import Vue from 'vue'
import Vuex from 'vuex'
import datas from '@/store/modules/datas'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  getters: {
  },
  modules: {
    datas
  }
})

export default store
<template>
  <div>
    <div>{{ $store.state.datas.uname }}</div>
    <button @click="updateUname">Mikanpapre</button>
  </div>
</template>

<script>
import { createNamespacedHelpers } from 'vuex'
const { mapMutations: userMapMutations } = createNamespacedHelpers('datas')
export default {
  methods: {
    ...userMapMutations(['updateName']),
    updateUname () {
      this.updateName('Mikanpapre')
    }
  }
}


Vue3 内容

相比 Vue2 它首次渲染更快、内存占用更少、diff 算法更快、打包体积更小、更好的支持 Typescript 拥有 Composition API 组合式 API,允许 template 存在多个根标签

vite 构建工具

vite 使用原生 ESModule 通过 script 标签动态导入,访问页面的时候加载到对应模块编译并响应,比 Webpack 查找依赖打包所有模块速度更快。在项目打包的时候还是得用打包工具 Rollup 打包成静态资源

运行创建项目:

# 使用 npm
npm create vite@latest
# 使用 yarn
yarn create vite

快速创建

# 使用 npm
npm init vite-app 项目名称
# 使用 yarn
yarn create vite-app 项目名称

Composition API

官方文档
Vue2 通过 datamethodswatch 等配置项编写代码逻辑是选项式 API 写法
Vue3 吧所有逻辑卸载 setup 函数中,使用 ref() computed() 等阻止代码是组合式 API 写法
组合式 API 可复用可维护


setup

setup 是组合式 API 的入口钩子,作为组合式 API 的起点,它在组件的生命周期 beforeCreate 之前执行,函数中的 this 不是组件实例而是 undefined 如果数据或者函数在 template 中使用需要 return 对象返回

export default {
  setup() {
    const uname = 'JuziYou'
    return { uname }
  }
}

setup 语法糖

Vue3.2 后在 <script> 标签上添加 setup 属性会可以省去默认暴露 export defaultreturn 步骤会自动完成,的顶层变量都可以在模板使用数据、函数、组件
setup 语法糖中 definePropsdefineEmitsdefineExpose 不需要再引入

setup 语法糖 name 问题

因为使用了 setup 的语法糖后无法使用选项式 API 所以可以使用 vite-plugin-vue-setup-extend 插件来给 <script> 标签上添加属性 name 来解决这个问题,<script> 标签内有内容才能生效

安装插件:

npm i vite-plugin-vue-setup-extend-plus -D

vite.config.ts 使用插件

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

// 引入 vite-plugin-vue-setup-extend-plus
import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus'

export default defineConfig({
  // plugins 中添加 vueSetupExtend() 使用插件
  plugins: [vue(), vueSetupExtend()],
})

然后在 <script> 标签添加 name 属性

<!-- script 标签添加 name 属性 -->
<script setup lang="ts" name="Nya">
  console.log('script 标签内有内容才能生效');
</script>

<template>
</template>

<style scoped>

</style>

ref

vue 中导出在 setup 钩子中使用一般传入一个简单数据类型或复杂数据类型,返回一个响应式数据,template外使用 ref 中的数据时需要 .value 调用,它可以定义任意数据的响应式

获取 DOM

vue 中导出在 setup 钩子中使用,ref会在onMounted生命周期后获取页面上绑定了与变量名相同 ref 属性的的 DOM 存在 .value 中,第一个参数是如果没有成功获取到 DOM 时的返回值

<template>
  <div ref="Nya">JuziYou</div>
</template>


<script setup>
  import { onBeforeMount, onMounted, ref } from 'vue'
  const Nya = ref(null)
  onBeforeMount(() => {
    console.log(Nya.value)
  })
  onMounted(() => {
    console.log(Nya.value)
  })
</script>

defineExpose

因为使用 <script setup> 的组件是默认关闭的,组件实例无法使用到顶层的数据和函数,需要配合 defineExpose 暴露给组件实例使用,暴露的响应式数据会自动解除响应式。
vue 中导出 defineExpose传入一个对象对象中写入需要导出的数据和函数

<template>
  <h1>{{ count }}</h1>
</template>

<script setup>
  import { ref, defineExpose } from 'vue'
  const count = ref(0)
  const countPlus = () => {
    count.value++
  }
  defineExpose({countPlus, count})
</script>
<template>
  <MyCount ref="count" />
  <button @click="addCount">Count++</button>
</template>


<script setup>
  import { ref } from 'vue';
  import MyCount from './components/MyCount.vue';
  const count = ref(null)
  const addCount = () => {
    count.value.countPlus()
  }
</script>


reactive

vue 中导出在 setup 钩子中使用, reactive 一般用来定义对象类型的响应式数据不支持简单数据类型,reactive 定义的数据修改不用加 .value 调用,不可以直接对它重新赋值,如果涉及到重新赋值应该使用 ref


toRefs

reactive 的响应式数据解构或者展开会失去响应式,使用 toRefs 函数让数据保持响应式, toRefs 会吧对象的每一个属性包装成响应式

<template>
  <div>{{ uname }} · {{ age }}</div>
  <button @click="age++">age++</button>
</template>


<script setup>
  import { reactive, toRefs } from 'vue'
  const person = reactive({
    uname: 'harry',
    age: 28
  })
  const {uname, age} = toRefs(person)
</script>

computed

vue 中导出在 setup 钩子中使用,computed 计算函数接收一个函数返回一个计算好的数据,并且和 Vue2 一样有缓存

<template>
  <div>{{ arr }}</div>
  <div>{{ sum }}</div>
  <button @click="pushNum">Push</button>
</template>


<script setup>
  import { ref, computed } from 'vue'
  const arr = ref([1, 1, 4, 5, 1, 4])
  const sum = computed(() => arr.value.reduce((pre, item) => pre + item, 0))
  const pushNum = () => {
    arr.value.push(1)
  }
</script>

watch

vue 中导出在 setup 钩子中使用,第三个值是配置项可以省略

单个数据监听

第一个参数是它接听的数据,第二个参数是函数数据发生变化执行的函数,函数中第一个接收值是新值第二个是旧值

<template>
  <div>{{ count }}</div>
  <button @click="numPlus">Plus</button>
</template>


<script setup>
  import { ref, watch } from 'vue'
  const count = ref(1)
  const numPlus = () => {
    count.value++
  }
  watch(count, (newVal, lodVal) => {
    console.log(newVal, lodVal);
  })
</script>

多个数据监听

在 Vue3 中 watch 支持一次监听多个数据,只需要第一个参数传入一个数组。第二个参数是函数数据发生变化执行的函数,函数中第一个接收值是新值第二个是旧值,返回值会是数组的形式

<template>
  <div>{{ count }} · {{ uname }}</div>
  <button @click="numPlus">Plus</button>
  <button @click="newName">NewName</button>
</template>


<script setup>
  import { ref, watch } from 'vue'
  const count = ref(1)
  const uname = ref('JuziYou')
  const numPlus = () => {
    count.value++
  }
  const newName = () => {
    uname.value = prompt('NewName')
  }
  watch([count, uname], (newVal, lodVal) => {
    console.log(newVal, lodVal);
  })
</script>

监听对象的单个属性

只需要第一个参数传入一个对象返回需要监听的属性。第二个参数是函数数据发生变化执行的函数,函数中第一个接收值是新值第二个是旧值

<template>
  <div>{{ user }}</div>
  <button @click="newName">NewName</button>
  <button @click="likePlus">LikePlus</button>
</template>


<script setup>
  import { watch , reactive } from 'vue'
  const user = reactive({
    uname: 'JuziYou',
    like: 0
  })
  const newName = () => {
    user.uname = prompt('NewName')
  }
  const likePlus = () => {
    user.like++
  }
  watch(()=>user.uname, (newVal, lodVal) => {
    console.log(newVal, lodVal);
  })
</script>

深度监听

和 Vue2 相同如果监听的是一个对象中的对象,发生变化后并不会触发,需要开启深度监听,在传入的第三个参数是配置项,在配置项中添加 deep: true 参数

<template>
  <div>{{ user }}</div>
  <button @click="likePlus">LikePlus</button>
</template>


<script setup>
  import { watch , reactive } from 'vue'
  const user = reactive({
    uname: 'JuziYou',
    info: {
      like: 0
    }
  })
  const likePlus = () => {
    user.info.like++
  }
  watch(()=>user.info, (newVal, lodVal) => {
    console.log(newVal, lodVal);
  }, {
    deep: true
  })
</script>

直接监听响应式对象

如果直接监听一个响应式对象时会自动启用深度监听

<template>
  <div>{{ user }}</div>
  <button @click="likePlus">LikePlus</button>
</template>


<script setup>
  import { watch , reactive } from 'vue'
  const user = reactive({
    uname: 'JuziYou',
    info: {
      like: 0
    }
  })
  const likePlus = () => {
    user.info.like++
  }
  watch(user, (newVal, lodVal) => {
    console.log(newVal, lodVal);
  })
</script>

生命周期

Vue3 的组合式 API 中与 Vue2 生命周期相比基本相同多了 on 打头 beforeCreatecreated 不再需要直接写入 setup 中即可,beforeDestroyeddestroyed 变成了 onBeforeUnmountonUnmounted
Vue3 中组合式 API 的生命周期函数可以在 setup 中多次调用,谁写在前面执行谁


组件通讯

官网文档

父向子通讯

与 Vue2 基本相同,不同在子组件在 setup 钩子中使用 defineProps 传入一个对象来接收父组件传来的值,如果需要在 script 中使用就接收返回值,如果不需要只在 template 中使用就不用接收

子向父通讯

与 Vue2 基本相同,不同在子组件在 setup 钩子中使用 defineEmits 传入的第一个值是一个数组里面每一项是要触发的父元素绑定的自定义事件名然后接收它的返回值,然后调用接收返回值的常量第一项是父元素绑定的自定义事件名第二项是传参

跨级组件通讯

通过通过 provideinject 函数实习便捷的跨级组件通讯,遵循谁提供由谁修改的原则
provide() 提供给后代的组件依赖或者数据,接收两个属性,第一个值为字符串第二个值为函数或要传递的数据
inject() 接收 provide() 提供给后代的组件依赖或者数据, 第一个值为 provide() 的第一个值(字符串)
详见官方文档

v-model

Vue3 中 v-model:modelValue@update:modelValue 的组合,Vue3官方文档
Vue3 中 v-model:name.sync 相同,相当于绑定了 :name@update:name


Pinia

Pinia 与 Vuex 一样是一个为 Vue 提供状态管理的工具,它与 Vue3 一样支持 选项式API组合式API,它也支持 Vue2 和 Devtools 和 TypeScript
可以创建多个全局仓库,不像 Vuex 一个仓库中嵌套模块解构复杂,而且数据管理比 Vuex 更简单只需要写提供数据和修改数据的逻辑即可,不需要像 Vuex 那样记忆大量 API
storeToRefs 可以解决在解构仓库里的数据后失去响应式的问题

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

// 2 调用 createPinia()
const pinia = createPinia()

const app = createApp(App)
// 3 注册 createPinia()
app.use(pinia)
app.mount('#app')
<script setup>
  // 10 引入 storeToRefs
  import { storeToRefs } from 'pinia'
  // 7 引入存储仓库的js文件中的存储
  import { useCountStore } from './store'
  // 8 使用一个变量存储调用按需引入的存储的返回值
  const store = useCountStore()
  // 11 storeToRefs 解决解构仓库里的数据失去响应式的问题
  const { count } = storeToRefs(store)
</script>

<template>
  <!-- 9 使用它 -->
  <div>{{ store.count }}</div>
  <div>{{ store.squCount }}</div>
  <div>{{ count }}</div>
  <button @click="store.addCont">count++</button>
  <button @click="store.asyncAddCount">asycCount++</button>
  <button @click="store.payloadCount(10)">asycCount+10</button>
</template>
// 4 引入 defineStore 来创建存储库
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'

// 5 defineStore 接收两个参数第一个是名字第二个是函数,函数中 return 出来一个对象里面是你的数据
export const useCountStore = defineStore('count', ()=>{
  // 5.1 定义响应式数据,相当于定义vuex中的state
  const count = ref(0)
  // 5.2 相当于定义了vuex中的getters
  const squCount = computed(()=> Math.pow(count.value, 2))
  // 5.3 定义修改响应式数据的方法,相当于定义vuex中的mutations
  const addCont = () => {
    count.value++
  }
  // 5.4 定义修改响应式数据异步加的方法,相当于定义vuex中的actions
  const asyncAddCount = () => {
    setTimeout(()=>{
      count.value++
    }, 1000)
  }
  // 5.5 接收参数
  const payloadCount = (num = 1) => {
    count.value += num
  }
  // 6 对外暴露它们
  return { count, squCount, addCont, asyncAddCount, payloadCount }
})


归档 文章二维码
本页链接的二维码
打赏二维码