Vue工程化开发

Vue工程化开发

一、工程化开发和脚手架

1.开发Vue的两种方式

  • 核心包传统开发模式:基于html / css / js 文件,直接引入核心包,开发 Vue。
  • 工程化开发模式:基于构建工具(例如:webpack)的环境中开发Vue。

工程化开发模式优点:

提高编码效率,比如使用JS新语法、Less/Sass、Typescript等通过webpack都可以编译成浏览器识别的ES3/ES5/CSS等

工程化开发模式问题:

  • webpack配置不简单
  • 雷同的基础配置
  • 缺乏统一的标准

为了解决以上问题,所以我们需要一个工具,生成标准化的配置

2.脚手架Vue CLI

Vue CLI 是Vue官方提供的一个全局命令工具

可以帮助我们快速创建 一个开发Vue项目的标准化基础架子。【集成了webpack配置】

使用步骤:

  1. 全局安装(只需安装一次即可) yarn global add @vue/cli 或者 npm i @vue/cli -g
  2. 查看vue/cli版本: vue --version
  3. 创建项目架子:vue create project-name(项目名不能使用中文)
  4. 启动项目:yarn serve 或者 npm run serve(命令不固定,找package.json)

3.项目目录介绍和运行流程

二、组件化开发

​ 组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。

​ 好处:便于维护,利于复用 → 提升开发效率。

​ 组件分类:普通组件、根组件。

1.根组件 App.vue

整个应用最上层的组件,包裹所有普通小组件

2.组件是由三部分构成

  • 语法高亮插件
  • 三部分构成

    • template:结构 (有且只能一个根元素)
    • script: js逻辑
    • style: 样式 (可支持less,需要装包)
  • 让组件支持less

    (1) style标签,lang="less" 开启less功能

    (2) 装包: yarn add less less-loader -D 或者npm i less less-loader -D

3.局部注册

局部注册只能在注册的组件内使用,放在components包下,以大驼峰命名法, 如 HmHeader

js 复制代码
// 导入需要注册的组件
import 组件对象 from '.vue文件路径'
import HmHeader from './componets/HmHeader'

export default {  // 局部注册
  components: {
   '组件名': 组件对象,
    HmHeader:HmHeaer,
    HmHeader
  }
}

3.全局注册

全局注册的组件,在项目的任何组件 中都能使用,在mian.js中进行注册

Vue.component('组件名', 组件对象)

例:

js 复制代码
// 导入需要全局注册的组件
import HmButton from './components/HmButton'
Vue.component('HmButton', HmButton)

scoped样式冲突

默认情况下,写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。

  1. 全局样式: 默认组件中的样式会作用到全局,任何一个组件中都会受到此样式的影响

  2. 局部样式 : 可以给组件加上scoped 属性,可以让样式只作用于当前组件

vue 复制代码
<style scoped>
</style>

组件data函数写法

一个组件的 data 选项必须是一个函数 (以前是对象)。保证每个组件维护一份数据,函数执行一次得到一个新函数保证每个组件实例,维护独立 的一份数据对象。

代码演示:

html 复制代码
<template>
  <div class="base-count">
    <button @click="count--">-</button>
    <span>{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      count: 100,
    }
  },
}
</script>

三、父子组件通信

组件通信,就是指组件与组件 之间的数据传递,组件的数据是独立的,无法直接访问其他组件的数据。想使用其他组件的数据,就需要组件通信

父子通信流程

  1. 父组件通过 props 将数据传递给子组件
  2. 子组件利用 $emit 通知父组件修改更新

1.定义父子组件

父组件App.vue

html 复制代码
<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是APP组件 
    <Son></Son>
  </div>
</template>

<script>
import Son from './components/Son.vue'
export default {
  name: 'App',
  data() {
    return {
      myTitle: 'Vue',
    }
  },
  components: {
    Son,
  },
}
</script>

子组件Son.vue

html 复制代码
<template>
  <div class="son" style="border:3px solid #000;margin:10px">
    我是Son组件
  </div>
</template>

<script>
export default {
  name: 'Son-Child',
}
</script>

<style>

</style>

2.父向子传递数据

子组件利用 props 向子组件传递数据,可以传递 任意数量 任意类型 的prop

父向子传值步骤

  1. 给子组件以添加属性的方式传值
  2. 子组件内部通过props接收
  3. 模板中直接使用 props接收的值

3.子向父通信

子组件利用 $emit 通知父组件,进行修改更新

子向父传值步骤

  1. $emit触发事件,给父组件发送消息通知
  2. 父组件监听$emit触发的事件
  3. 提供处理函数,在函数的性参中获取传过来的参数

4.props校验

为组件的 prop 指定验证要求 ,不符合要求,控制台就会有错误提示 → 帮助开发者,快速发现错误,可以进行类型校验、非空校验、默认值、自定义校验

css 复制代码
props:{
	校验的属性名:类型(Number String Boolean ...)//传递值必须为对应类型
}

props校验完整写法

语法:

js 复制代码
props: {
  校验的属性名: {
    type: 类型,  // Number String Boolean ...
    required: true, // 是否必填
    default: 默认值, // 默认值
    validator (value) {
      // 自定义校验逻辑
      return 是否通过校验
    }
  }
},

注意:

  • default和required一般不同时写(因为当时必填项时,肯定是有值的)

  • default后面如果是简单类型的值,可以直接写默认。如果是复杂类型的值,则需要以函数的形式return一个默认值

四、-event bus事件总线

-event bus事件总线可在非父子组件之间,进行简易消息传递。(复杂场景→ Vuex)

步骤

  1. 创建一个都能访问的事件总线 (空Vue实例)

    js 复制代码
    import Vue from 'vue'
    const Bus = new Vue()
    export default Bus
  2. A组件(接受方),监听Bus的 $on事件

    js 复制代码
    created () {
      Bus.$on('sendMsg', (msg) => {
        this.msg = msg
      })
    }
  3. B组件(发送方),触发Bus的$emit事件

    js 复制代码
    Bus.$emit('sendMsg', '这是一个消息')

代码示例

五、provide&inject

provide&inject可用于跨层级共享数据,顶层组件提供数据,子孙后代获取数据

语法

  1. 父组件 provide提供数据
js 复制代码
export default {
  provide () {
    return {
       // 普通类型【非响应式】
       color: this.color, 
       // 复杂类型【响应式】
       userInfo: this.userInfo, 
    }
  }
}

2.子/孙组件 inject获取数据

js 复制代码
export default {
  inject: ['color','userInfo'],
  created () {
    console.log(this.color, this.userInfo)
  }
}

注意

  • provide提供的简单类型的数据不是响应式的,上层修改子孙后代的值不会实时修改,复杂类型数据是响应式。(推荐提供复杂类型数据)
  • 子/孙组件通过inject获取的数据,不能在自身组件内修改

六、.sync修饰符

作用:实现 子组件父组件数据双向绑定,简化代码

简单理解:子组件可以修改父组件传过来的props值

语法

父组件在原本父传子的基础上加上 .sync@update:属性名的简写

html 复制代码
//.sync写法,本质是 :属性名和
<BaseDialog :visible.sync="isShow" />
--------------------------------------
//完整写法
<BaseDialog 
  :visible="isShow" 
  @update:visible="isShow = $event" 
/>

子组件事件类型固定位update:属性名

js 复制代码
props: {
  visible: Boolean
},
//update对应上面的@update
this.$emit('update:visible', false)

代码示例

七、ref和$refs

ref 和 $refs 可以用于在 **当前组件内 **(更精确稳定)获取 dom 元素 或 组件实例

语法

1.给要获取的盒子添加ref自定义属性名

html 复制代码
<div ref="chartRef">我是渲染图表的容器</div>

2.获取时通过 refs获取 this.refs.chartRef 获取(当前实例须渲染完成)

js 复制代码
mounted () {
  console.log(this.$refs.chartRef)
}

之前只用document.querySelect('.box') 获取的是整个页面中的盒子

代码示例

八、异步更新 & $nextTick

$refs获取dom时,可能dom未更新导致获取失败,$nextTick等 DOM更新后,才会触发执行此方法里的函数体

语法: this.$nextTick(函数体)

js 复制代码
this.$nextTick(() => {
  this.$refs.inp.focus()
})

注意: $nextTick 内的函数体 一定是箭头函数,这样才能让函数内部的this指向Vue实例

九、插槽

1.默认插槽

如果组件结构相同,但现实内容不同,例如提示框。就可以使用插槽自定义组件显示内容

插槽的基本语法

  1. 组件内需要定制的结构部分,改用****占位
  2. 使用组件时, ****标签内部, 传入结构替换slot
  3. 给插槽传入内容时,可以传入纯文本、html标签、组件

实例:

插槽默认值

通过插槽完成了内容的定制,传什么显示什么, 但是如果不传,则是空白

封装组件时,可以为预留的 <slot> 插槽提供后备内容(默认内容)。

在 标签内,放置内容, 作为默认显示内容

js 复制代码
 <!-- 往slot标签内部,编写内容,可以作为后备内容(默认值) -->
 <slot>我是默认的文本内容</slot>

2.插槽-具名插槽

默认插槽只能定制一个内容,如果需要为多个地方定制内容,需要使用具名插槽(给插槽起名字)

语法

v-slot写起来太长,vue给我们提供一个简单写法 v-slot ---> #

3.作用域插槽

作用域插槽值定义slot 插槽的同时, 。给 插槽 上可以 绑定数据 ,来 给使用组件时可以用,它是插槽的传参语法,并不是插槽的类别

使用步骤

  1. 给 slot 标签, 以 添加属性的方式传值

    html 复制代码
    <slot :id="item.id" msg="测试文本"></slot>
  2. 所有添加的属性, 都会被收集到一个对象中

    js 复制代码
    { id: 3, msg: '测试文本' }
  3. 在template中, 通过 #插槽名= "obj" 接收,默认插槽名为 default

    html 复制代码
    <MyTable :list="list">
      <template #default="obj">
        <button @click="del(obj.id)">删除</button>
      </template>
    </MyTable>

插槽只有两种,默认插槽和具名插槽,作用域插槽不属于插槽的一种分类

十、路由

1.路由基本使用

Vue工程一般都为单页面程序,单页应用程序:SPA【Single Page Application】是指所有的功能都在一个html页面 上实现,如网易云官网。单页面应用程序页面是按需更新,所以开发效率高,性能好,用户体验也更好。要按需更新,就需要明确:访问路径组件的对应关系,而路由就是设置组件和访问路径的

VueRouter安装配置步骤

js 复制代码
//1.下载 VueRouter 模块到当前工程,版本3.6.5
yarn add [email protected]
//2.main.js中引入VueRouter
import VueRouter from 'vue-router'
//3.安装注册
Vue.use(VueRouter)
//4.创建路由对象
const router = new VueRouter()
//5.注入,将路由对象注入到new Vue实例中,建立关联
new Vue({
  render: h => h(App),
  router:router
}).$mount('#app')

注:当我们配置完以上5步之后 就可以看到浏览器地址栏中的路由 变成了 /#/的形式。表示项目的路由已经被Vue-Router管理了

VueRouter路径设置

  1. main.js创建需要的组件 (views目录),配置路由规则

    js 复制代码
    import Find from './views/Find'
    import My from './views/My'
    import Friend from './views/Friend'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter) // VueRouter插件初始化
    
    const router = new VueRouter({
      // routes 路由规则们
      // route  一条路由规则 { path: 路径, component: 组件 }
      routes: [
        { path: '/find', component: Find },
        { path: '/my', component: My },
        { path: '/friend', component: Friend },
      ]
    })
  2. 配置导航,配置路由出口(路径匹配的组件显示的位置)

    App.vue

    html 复制代码
    <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>

路由的封装抽离

可以将路由模块单独抽离出来。 好处:拆分模块,利于维护

路径简写:

脚手架环境下 @指代src目录,可以用于快速引入组件

2.声明式导航-导航链接

vue-router 提供了一个全局组件 router-link (取代 a 标签)

  • 能跳转 ,配置 to 属性指定路径(必须 ) 。本质还是 a 标签 ,to 无需 #
  • 能高亮 ,默认就会提供高亮类名,可以直接设置高亮样式

语法: 发现音乐

html 复制代码
  <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 class="top">
      <!-- 路由出口 → 匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>
  </div>

声明式导航-两个类名

使用router-link跳转后,我们发现。当前点击的链接默认加了两个class的值 router-link-exact-activerouter-link-active我们可以给任意一个class属性添加高亮样式即可实现功能

  • router-link-active:模糊匹配(用的多,to="/my" 可以匹配 /my /my/a /my/b ...

  • router-link-exact-active:精确匹配,to="/my" 仅可以匹配 /my

-在跳转路由时,进行传参。可以通过查询参数传参动态路由传参两种方式,在跳转的时候把所需要的参数传到其他页面中

01-查询参数传参

  • 传参:

  • 接受参数:

    固定用法:$router.query.参数名

示例

02-动态路由传参

  • 配置动态路由

    动态路由后面的参数可以随便起名,但要有语义

    js 复制代码
    const router = new VueRouter({
      routes: [
        ...,
        { 
          path: '/search/:words', 
          component: Search 
        }
      ]
    })
  • 配置导航链接

    to="/path/参数值"

  • 对应页面组件接受参数

    $route.params.参数名

示例

注:

  1. 查询参数传参 (比较适合传多个参数)
  2. 动态路由传参 (优雅简洁,传单个参数比较方便)
  3. 动态路由也可以传多个参数,但一般只传一个

动态路由参数的可选符(了解)

配了路由 path:"/search/:words" 如果没有传入参数,会未匹配到组件显示空白。如果不传参数,也希望匹配,可以在words后面加一个

3.编程式导航

编程式导航是用JS代码来实现点击按钮时跳转路由,主要有两种方式:path 路径跳转 (简易方便)和name 命名路由跳转 (适合 path 路径长的场景)

01-path路径跳转语法

特点:简易方便

  • query查询参数跳转
js 复制代码
//简单写法
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2')
//完整写法
this.$router.push({
  path: '/路径',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

接受参数的方式依然是:$route.query.参数名

  • 动态路由传参
js 复制代码
//简单写法
this.$router.push('/路径/参数值')
//完整写法
this.$router.push({
  path: '/路径/参数值'
})

示例

**注意:**path不能配合params使用

02-name命名路由跳转

特点:适合 path 路径长的场景

  • query查询参数跳转
js 复制代码
this.$router.push({
  name: '路由名字',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

接受参数的方式依然是:$route.query.参数名

  • 动态路由传参
js 复制代码
this.$router.push({
  name: '路由名字',
  params: {
    参数名: '参数值',
  }
})

示例

4.路由重定向

网页打开时, url 默认是 / 路径,未匹配到组件时,会出现空白,可以加上redirect配置重定向路径

示例

js 复制代码
const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home'},
 	 ...
  ]
})

5.404页面路由

当路径找不到匹配时,给个404提示页面

配置方式

path: "*" (任意路径) -- 前面不匹配就命中最后这个

js 复制代码
import NotFind from '@/views/NotFind'

const router = new VueRouter({
  routes: [
    ...
    { path: '*', component: NotFind } //最后一个
  ]
})

6.Vue路由-模式设置

路由的路径看起来不自然, 有#,能否切成真正路径形式?

设置方式

js 复制代码
const router = new VueRouter({
    mode:'histroy', //默认是hash
    routes:[]
})

7. 二级路由配置

当在页面中点击链接跳转,只是部分内容切换时,我们可以使用嵌套路由,二级路由也叫嵌套路由,当然也可以嵌套三级、四级...

配置方式:

  • 在一级路由下,配置children属性即可
  • 配置二级路由的出口

1.在一级路由下,配置children属性

注意 :一级的路由path 需要加 / 二级路由的path不需要加 /

js 复制代码
const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: Layout,
      children:[
        //children中的配置项 跟一级路由中的配置项一模一样 
        {path:'xxxx',component:xxxx.vue},
        {path:'xxxx',component:xxxx.vue},
      ]
    }
  ]
})

2.配置二级路由的出口

这些二级路由对应的组件渲染到哪个一级路由下,children就配置到哪个路由下边

注意: 配置了嵌套路由,一定配置对应的路由出口,否则不会渲染出对应的组件

Layout.vue

html 复制代码
<template>
  <div class="h5-wrapper">
    <div class="content">
      <router-view></router-view>
    </div>
  ....
  </div>
</template>

二级导航高亮

  • 将a标签替换成 组件,配置to属性,不用加 #
  • 结合高亮类名实现高亮效果 (推荐模糊匹配:router-link-active)

代码实现

Layout.vue

html 复制代码
....
    <nav class="tabbar">
      <router-link to="/article">面经</router-link>
      <router-link to="/collect">收藏</router-link>
      <router-link to="/like">喜欢</router-link>
      <router-link to="/user">我的</router-link>
    </nav>

<style>
   a.router-link-active {
      color: orange;
    }
</style>

十一、缓存组件

当路由被跳转 后,原来所看到的组件就被销毁 了(会执行组件内的beforeDestroy和destroyed生命周期钩子),重新返回 后组件又被重新创建 了(会执行组件内的beforeCreate,created,beforeMount,Mounted生命周期钩子),所以数据被加载了。这样就会导致详情页返回列表页,页面重新加载回到列表顶部。

keep-alive

keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。在组件切换过程中把切换出去的组件保留在内存中,防止重复渲染DOM,可以减少加载时间及性能消耗,提高用户体验性。

keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件中。

App.vue

html 复制代码
<template>
  <div class="h5-wrapper">
    <keep-alive>
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

这样会缓存了所有被切换的组件

keep-alive的三个属性

  1. include : 组件名数组,只有匹配的组件会被缓存

  2. exclude : 组件名数组,任何匹配的组件都不会被缓存

  3. max : 最多可以缓存多少组件实例

示例:

html 复制代码
<template>
  <div class="h5-wrapper">
    <keep-alive :include="['LayoutPage']">
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

keep-alive的使用会触发两个生命周期函数

  • activated 当组件被激活(使用)的时候触发 → 进入这个页面的时候触发
  • deactivated 当组件不被使用的时候触发 → 离开这个页面的时候触发

组件缓存后不会执行 组件的created, mounted, destroyed 等钩子了

所以其提供了actived 和deactived钩子,帮我们实现业务需求。

十二、Vuex

Vuex 是一个 Vue 的 状态管理工具,状态就是数据。可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。

优势:

  • 共同维护一份数据,数据集中化管理
  • 响应式变化
  • 操作简洁 (vuex提供了一些辅助函数)

但不是所有的场景都适用于vuex,只有在必要的时候才使用vuex,使用了vuex之后,会附加更多的框架中的概念进来,增加了项目的复杂度 (数据的操作更便捷,数据的流动更清晰)

vuex安装使用

1. 安装 vuex

js 复制代码
//安装vuex与vue-router类似,vuex是一个独立存在的插件,如果脚手架初始化没有选 vuex,就需要额外安装。
yarn add vuex@3 或者 npm i vuex@3

2.新建 store/index.js 专门存放 vuex

​ 为了维护项目目录的整洁,在src目录下新建一个store目录其下放置一个index.js文件。 (和 router/index.js 类似)

3.创建仓库 store/index.js

js 复制代码
// 导入 vue
import Vue from 'vue'
// 导入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 进行插件的安装初始化
Vue.use(Vuex)

// 创建仓库 store
const store = new Vuex.Store()

// 导出仓库
export default store

4 在 main.js 中导入挂载到 Vue 实例上

js 复制代码
import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

此刻起, 就成功创建了一个 空仓库!!

5.测试打印Vuex

App.vue

js 复制代码
created(){
  console.log(this.$store)
}

1.state 状态

State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储。

打开项目中的store.js文件,在state对象中可以添加我们要共享的数据。

js 复制代码
// 创建仓库 store
const store = new Vuex.Store({
  // state 状态, 即数据, 类似于vue组件中的data,
  // 区别:
  // 1.data 是组件自己的数据, 
  // 2.state 中的数据整个vue项目的组件都能访问到
  state: {
    count: 101
  }
})

访问Vuex中的数据的方式:

  1. 通过store直接访问 ---\> {{ store.state.count }}
  2. 通过辅助函数mapState 映射计算属性 ---> {{ count }}

通过$store访问的语法

js 复制代码
获取 store:
 1.Vue模板中获取 this.$store
 2.js文件中获取 import 导入 store


模板中:     {{ $store.state.xxx }}
组件逻辑中:  this.$store.state.xxx
JS模块中:   store.state.xxx

组件逻辑中使用

将state属性定义在计算属性中,可以直接使用变量名访问

js 复制代码
<h1>state的数据 - {{ count }}</h1>

// 把state中数据,定义在组件内的计算属性中
  computed: {
    count () {
      return this.$store.state.count
    }
  }

每次都像这样一个个的提供计算属性, 太麻烦了,我们有没有简单的语法帮我们获取state中的值呢?

2.mapState

访问state内属性每次都需要一个个的提供计算属性, 太麻烦了。Vue为我们提供了mapState辅助函数,帮助我们把store中的数据映射到 组件的计算属性中

使用步骤

js 复制代码
//1.引入 mapState函数
  import { mapState } from 'vuex'
//2.采用数组形式引入state属性
  mapState(['count']) 
//3.利用展开运算符将导出的状态映射给计算属性
  computed: {
    ...mapState(['count'])
  }

3.mutations

明确 vuex 同样遵循单向数据流,组件中不能直接修改仓库的数据。如果需要修改,需要提交mutations方式提交给仓库让仓库进行修改

mutations是一个对象,对象中存放修改state的方法

js 复制代码
const store  = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    // 方法里参数 第一个参数是当前store的state属性
    // payload 载荷 运输参数 调用mutaiions的时候 可以传递参数 传递载荷
    addCount (state) {
      state.count += 1
    }
  }
})

组件中提交 mutations

js 复制代码
this.$store.commit('addCount')

带参数的 mutations

1.提供mutation函数(带参数)

js 复制代码
mutations: {
  ...
  addCount (state, count) {
    state.count = count
  }
},

2.提交mutation

js 复制代码
handle ( ) {
  this.$store.commit('addCount', 10)
}

小tips: 提交的参数只能是一个, 如果有多个参数要传, 可以传递一个对象

4.mapMutations

mapMutations和mapState很像,它把位于mutations中的方法提取了出来,我们可以将它导入

js 复制代码
import  { mapMutations } from 'vuex'
methods: {
    ...mapMutations(['addCount'])
}
//上面代码的含义是将mutations的方法导入了methods中,等价于
//methods: {
//      addCount () {
//          this.$store.commit('addCount')
//      }
// }
js 复制代码
methods: {
      // commit(方法名, 载荷参数)
      addCount () {
          this.$store.commit('addCount')
      }
 }

此时,就可以直接通过this.addCount调用了

jsx 复制代码
<button @click="addCount">值+1</button>

注意: Vuex中mutations须是同步的,不能写异步代码,如果有异步的ajax请求,应该放置在actions中

5.actions

state是存放数据的,mutations是同步更新数据 (便于监测数据的变化, 更新视图等, 方便于调试工具查看变化),

actions则负责进行异步操作

1.定义actions

js 复制代码
mutations: {
  changeCount (state, newCount) {
    state.count = newCount
  }
}


actions: {
  setAsyncCount (context, num) {
    // 一秒后, 给一个数, 去修改 num
    setTimeout(() => {
      context.commit('changeCount', num)
    }, 1000)
  }
},

2.组件中通过dispatch调用

js 复制代码
setAsyncCount () {
  this.$store.dispatch('setAsyncCount', 666)
}

6.mapActions

mapActions 是把位于 actions中的方法提取了出来,映射到组件methods中

js 复制代码
import { mapActions } from 'vuex'
methods: {
   ...mapActions(['changeCountAction'])
}

//mapActions映射的代码 本质上是以下代码的写法
//methods: {
//  changeCountAction (n) {
//    this.$store.dispatch('changeCountAction', n)
//  },
//}

直接通过 this.方法 就可以调用

html 复制代码
<button @click="changeCountAction(200)">+异步</button>

7.getters/mapGetters

除了state之外,有时我们还需要从state中筛选出符合条件的一些数据,这些数据是依赖state的,此时会用到getters。getters的数据依赖state

1.定义getters

js 复制代码
  getters: {
    // getters函数的第一个参数是 state
    // 必须要有返回值
     filterList:  state =>  state.list.filter(item => item > 5)
  }

2.使用getters

  • 2.1原始方式-$store
html 复制代码
<div>{{ $store.getters.filterList }}</div>
  • 2.2辅助函数 - mapGetters
js 复制代码
computed: {
    ...mapGetters(['filterList'])
}
html 复制代码
 <div>{{ filterList }}</div>

十三、Vuex分模块:module

由于使用单一状态树 ,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。这句话的意思是,如果把所有的状态都放在state中,当项目变得越来越大的时候,Vuex会变得越来越难以维护。由此,又有了Vuex的模块化

分模块步骤:

01.模块定义 - 准备 state

定义两个模块 usersetting 放在 store/modules/user.js,并导出

js 复制代码
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  }
}

const mutations = {}
const actions = {}
const getters = {}
export default {
  namespace:true //开启命名空间
  state,
  mutations,
  actions,
  getters
}

02.模块注册

store/index.js文件中的modules配置项中,注册这两个模块

js 复制代码
import user from './modules/user'
import setting from './modules/setting'

const store = new Vuex.Store({
    modules:{
        user,
        setting
    }
})

使用模块中的数据, 可以直接通过模块名访问 $store.state.模块名.xxx => $store.state.setting.desc

也可以通过 mapState 映射

尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的 state 中,属性名就是模块名

使用模块中的State

  1. 直接通过模块名访问 $store.state.模块名.xxx
  2. 通过 mapState 映射:
    1. 默认根级别的映射 mapState([ 'xxx' ]) (如mapState([ 'user' ]是获取整个子模块的对象)
    2. 子模块的映射 :mapState('模块名', ['xxx']) - 需要开启命名空间 namespaced:true

获取模块的getters

  1. 直接通过模块名访问 $store.getters['模块名/xxx ']
  2. 通过 mapGetters 映射
    1. 默认根级别的映射 mapGetters([ 'xxx' ])
    2. 子模块的映射 mapGetters('模块名', ['xxx']) - 需要开启命名空间

获取模块内的mutations

  1. 直接通过 store 调用 $store.commit('模块名/xxx ', 额外参数)
  2. 通过 mapMutations 映射
    1. 默认根级别的映射 mapMutations([ 'xxx' ])
    2. 子模块的映射 mapMutations('模块名', ['xxx']) - 需要开启命名空间

获取模块内的actions

  1. 直接通过 store 调用 $store.dispatch('模块名/xxx ', 额外参数)
  2. 通过 mapActions 映射
    1. 默认根级别的映射 mapActions([ 'xxx' ])
    2. 子模块的映射 mapActions('模块名', ['xxx']) - 需要开启命名空间

onst mutations = {}

const actions = {}

const getters = {}

export default {

namespace:true //开启命名空间

state,

mutations,

actions,

getters

}

复制代码
02.模块注册

在`store/index.js`文件中的modules配置项中,注册这两个模块

```js
import user from './modules/user'
import setting from './modules/setting'

const store = new Vuex.Store({
    modules:{
        user,
        setting
    }
})

使用模块中的数据, 可以直接通过模块名访问 $store.state.模块名.xxx => $store.state.setting.desc

也可以通过 mapState 映射

尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的 state 中,属性名就是模块名

使用模块中的State

  1. 直接通过模块名访问 $store.state.模块名.xxx
  2. 通过 mapState 映射:
    1. 默认根级别的映射 mapState([ 'xxx' ]) (如mapState([ 'user' ]是获取整个子模块的对象)
    2. 子模块的映射 :mapState('模块名', ['xxx']) - 需要开启命名空间 namespaced:true

获取模块的getters

  1. 直接通过模块名访问 $store.getters['模块名/xxx ']
  2. 通过 mapGetters 映射
    1. 默认根级别的映射 mapGetters([ 'xxx' ])
    2. 子模块的映射 mapGetters('模块名', ['xxx']) - 需要开启命名空间

获取模块内的mutations

  1. 直接通过 store 调用 $store.commit('模块名/xxx ', 额外参数)
  2. 通过 mapMutations 映射
    1. 默认根级别的映射 mapMutations([ 'xxx' ])
    2. 子模块的映射 mapMutations('模块名', ['xxx']) - 需要开启命名空间

获取模块内的actions

  1. 直接通过 store 调用 $store.dispatch('模块名/xxx ', 额外参数)
  2. 通过 mapActions 映射
    1. 默认根级别的映射 mapActions([ 'xxx' ])
    2. 子模块的映射 mapActions('模块名', ['xxx']) - 需要开启命名空间
相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom11 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试