MVVM

image.png

MVVM 分为 Model、View、ViewModel:

  • Model代表数据模型,数据和业务逻辑都在Model层中定义;
  • View代表UI视图,负责数据的展示;
  • ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;

Model和View并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。

vue

1.数据与视图分离 (数据驱动视图)

2.性能更高,减少dom操作(耗费性能) diff (虚拟dom对比)

3.组件化

虚拟DOM的优缺点:

  • 缺点
    • 首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢
  • 优点
    • 减少了dom操作,减少了回流与重绘
    • 保证性能的下限,虽说性能不是最佳,但是它具备局部更新的能力,所以大部分时候还是比正常的DOM性能高很多的

双大括号插值,值一定是data中的变量 (数据)

双大括号其实提供了一个js执行环境,可以写任意js表达式,不识别html结构

指令:都是以v-开头的,去实现特定功能,写在元素开始标签中的

注意:所有指令后面的引号是提供一个js执行环境的,不是字符串的引号,写字符串必须加单引号;

1.v-html="数据" 把一段html结构渲染到它所绑定的元素中 (识别标签)

2.v-text = {{}}

3.v-bind:属性="数据" 简写为 :属性="数据" (动态绑定变量)

特殊情况:class

        1.可以绑定一个字符串,字符串名就是class名

        2.可以绑定一个对象

                对象的属性名就是class名,对象的属性值是布尔,代表是否有这个class

        3.可以绑定一个数组

        数组里是变量名,变量值就是class名字

特殊情况:style

        1.绑定到一个对象,对象就是样式对象

        2.绑定到一个数组,就是绑定到多个样式对象

        3.v-on:事件名="函数名" 缩写为@事件名="函数名"

样式穿透:

父元素 ::v-deep 内部元素(通过f12查看元素类名)

//如果使用的是css,可以用下面这种

外层容器 >>> 组件 {}

//但在css预处理器中用上面这种是无法生效的,类似在scss和less中,我们可以用下面这种。

外层容器 /deep/ 组件 {}

method函数中,要想改数据的话,不要用箭头函数

this.变量名=新数据

this代表的是当前vue实例 

模板中拿不到window对象/vue实例

只有传参函数名才可以加()

引号中的变量必须是data中或methods中定义过的;

事件对象:

image.png

5.v-if v-else-if v-else条件渲染 真正的条件渲染 

6.v-show 条件渲染 只是基于样式的切换

区别:v-show有更高的初始渲染消耗 v-if有更高的切换消耗

7.v-for=“item in array” 列表渲染

可以循环一个数组,item代表每一项 (item,index)

如果需要修改,选择数组自带的方法,或者使用Vue.set(要修改的数组,角标,新值)

直接重新赋值

可以循环一个对象,item代表每一项的值 (value,key,index)

如果需要添加对象属性 Vue.set(要修改的对象,属性名,属性值)

可以循环一个整数

嵌套循环(嵌套循环避免变量重复)

v-for优先级高于v-if

避免v-if和v-for一起用:

可以v-if嵌套v-for

如果非要一起用,可以把v-if放到外层元素,或者不用v-if,先在计算属性中筛选出数据,然后在v-for

<template>
  <div id="app">
    <ul>
      <li v-for="item in info" :key={item}>{{item}}->偶数</li>
    </ul>
  </div>
</template>
<script>
export default {
  data () {
    return {
      Nums: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    }
  },
  computed: {
    info () {
      return this.Nums.filter(i => i % 2 === 0)
    }
  }
}
</script>

v-for必须定义key:

        不能用index和random

        diff算法通过tag和key判断是否是sameNode

        减少渲染次数,提高渲染性能

8.v-model:表单的双向绑定

v-model默认绑定到表单的value属性

多选框: 单个复选框,绑定到布尔值,多个复选框,绑定的是数组

单选框:值直接绑定到值

下拉选择框:直接绑定到值

实现:oninput/onchange事件      事件对象

v-model的原理

vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法。

 // var obj = {
        //     a: 1,
        //     c: [1, 2, 3],
        //     set f(value) {
        //         console.log("set")
        //         this.a = value
        //     },
        //     get f() {
        //         console.log("get")
        //         return this.a
        //     }
        // }
        // Object.defineProperty(obj, "h", {
        //     value: 10
        // })
 
        // var obj1 = JSON.parse(JSON.stringify(obj))
        // console.log(obj)
        // console.log(obj1)
 
        var obj={a:1,b:2};
        var hander={
            set:(target,key,value)=>{
                console.log("set",target,key,value)
                target[key]=value
            },
            get:(target,key)=>{
                console.log("get")
                return target[key]
            },
            has:(target,key)=>{
                console.log("has")
                return key in target
            }
        }
        var p=new Proxy(obj,hander);
        p.c=20;
        console.log(p.c)
        console.log("c" in p)

methods:里面写所有的函数 (事件)
computed:计算属性:将逻辑运算写在这里,避免在模板里面写过多js逻辑

计算属性是个函数,一定需要return一个值的

计算属性里面的函数一定是她所依赖的值发生变化的才会去执行 (提高性能)

避免计算属性与data中数据重复

过滤器:filter

watch:监听器 用来监测一个数据,只要数据发生变化,就会执行一个函数

函数名为数据名

watch都可以监听哪些值:

1.data中的数据
2.props
3.$route
4.$emit
5.computed

computed与methods,watch区别:

  • method 调用总会执行该函数。数据驱动视图(视图重新渲染) 主动调用 做事
  • computed 计算属性 :有缓存,只有在它的相关依赖发生改变时会自动重新求值,适用于大量计算的场景  当一个属性受多个属性影响的时候使用
  • watch 侦听器 : 无缓存,每当监听的数据变化时都会执行回调进行后续操作。 做事 一次只能监听一个,页面重新渲染时值不变化也会执行;当一条数据影响多条数据的时候使用
<template>
  <div>
    <p>{{msg}}</p>
    <p @click="change">改变</p>
    <p>{{info}}</p>
  </div>
</template>
<script>
  export default {
    data(){
      return {
        msg:111,
      }
    },
    methods:{
      change(){
        this.msg+=1
      }
    },
    watch:{
        msg: {
            handler(oldValue,newVal) {
                console.log("msg",oldValue,newVal)
            },
            deep: true  // 深度监听
        }
    },
    computed:{
      info(old,new){
        return this.msg
      }
    },
  }
</script>

事件修饰符

  • .stop:等同于 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;
  • .prevent :等同于 JavaScript 中的 event.preventDefault() ,防止执行预设的行为
  • .enter
  • .trim

组件

只能使用自己的配置项(data,methods,watch,computend,components)

组件的注册

1.全局注册:

Vue.component("组件名",{
    template:"html代码",
    data(){
        return{}
    }
})

其他的完全跟vue实例一样的

注意:

data必须是函数,保证组件之间的数据必须独立性,根实例只有一个(数据为对象)

对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改,而是用返回对象的函数,每次返回的嗾使一个新对象,引用地址不同,不会出现这个问题;

template必须只有一个根节点

2.局部注册:

1.先定义组件配置选项,

2.在要使用的组件中去注册

<div id="app">
    <my-foot></my-foot>
</div>
<script>
let foot = { 
    template: `<div><h1 >{{msg}}</h1></div>`, 
    data() { return { msg: "局部组件" } }, 
}
new Vue({
    el:"#app",
    components:{
        "my-foot":foot     //局部注册
    }
})  
Vue.component("foot",foot)   //全局注册
</script>

3.组件之间的数据传递(父子组件的单向数据流)

(1)父--子 props

  • 父组件动态绑定:src=src
  • 子组件定义props
  • 子组件规定该属性用在哪
  • 组件调用在开始标签里面传递来自父组件的数据

props:可以是数组,可以是对象{属性名:类型}(对象可以对数据进行检测)

(2)子--父:自定义事件$emit

  • 在父组件调用的子组件开始标签绑定一个自定义事件@src=“src(msg)”
  • 在该子组件内部定义方法,该方法要通过$emit去触发自定义事件,并且把自己的数据作为$emit的第二个参数传递进去  $emit(@src,"222")
  • 自定义事件触发时要做的事定义在父组件的methods中,在此方法里只需要将自己的数据更新为传进来的参数即可

(3)ref / $refs(dom操作)

ref: 这个属性用在子组件上,它的引用就指向了子组件的实例。可以通过实例来访问组件的数据和方法。

<child ref="child"></component-a>

 console.log(this.$refs['child']);

通过 ref 属性给子组件设置一个名字。父组件通过 $refs 组件名来获得子组件,子组件通过 $parent 获得父组件,这样也可以实现通信。

$nextTick()方法:

异步渲染,待dom渲染完毕后再回调

this.$nextTick(() => { console.log(222); console.log(this.$refs['hello']); });

(4)事件总线

import Vue from 'vue'
var bus=new Vue()

发布
<p @click="get">发布</p>
 get(){
        bus.$emit("get",this.msgg)
      }

订阅
 bus.$on("get",(data)=>{
                this.ms=data
            })

及时销毁  否则可能内存泄漏
beforeDestroy(){
    bus.$off("get",this.msgg)
}

获取自定义属性的属性值

第一种:

<div data-msg="222" ref="ww"></div>

this.$refs.ww.dataset.msg

第二种:

<span data-num="21" @click="getData($event)">55</span>

e.target.getAttribute('data-num')

4.slot

slot又名插槽,是Vue的内容分发机制,组件标签中的所有内容会渲染到插槽位置上,让组件更灵活

具名插槽:没有名字的插槽默认名字是default

v-slot可以缩写为#

作用域插槽:

如果插槽模板中想使用该组件内部的数据,首先需要在模板的插槽中定义v-bind:属性=“数据”

 <slot name="before"  :ss="'2222'"></slot>

组件调用的地方插槽通过 v-slot:属性=“prop” prop代表所有属性的集合

<template v-slot:before="prop">

      <h1>{{prop.ss}}</h1>

 </template>

template 包裹标签,运行时不会被渲染

//调用:
<jyn-button>插槽</jyn-button>
//jyn-button.vue
<div>
	<slot></slot>
</div>

具名插槽:
//调用:
<jyn-button>
    <template v-slot="a">
        插槽
    </template>
</jyn-button>
//jyn-button.vue
<div>
  <slot name="a"></slot>
</div>

5.组件的生命周期:其实就是在某个时间段会自动触发的事件

Vue 实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。

  1. beforeCreate(创建前):创建实例前
  2. created(创建后) :实例创建完成, data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM
  3. beforeMount(挂载前):编译模板,把data里面的数据和模板生成html
  4. mounted(挂载后):挂载DOM对象
  5. beforeUpdate(更新前):响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
  6. updated(更新后) :此时 DOM 已经根据响应式数据的变化更新了
  7. beforeDestroy(销毁前):实例销毁之前调用。this 仍能获取到实例。
  8. destroyed(销毁后):实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。(解绑自定义事件,清除定时器,解绑自定义的dom事件)

keepalive:

keep-alive是vue内置的一个组件,而这个组件的作用就是能够缓存不活动的组件

要使用activated与deactivated生命周期来获取当前组件是否处于活动状态

vue父子组件生命周期的执行顺序(面试中问道)

加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated

父组件更新过程
父beforeUpdate->父updated

销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

动态组件

<component :is = "temp"></component>

vue/cli搭建vue脚手架的步骤:

npm install -g @vue/cli 全局安装vue脚手架

vue create 项目名字

脚手架生成文件说明

image.png

  • 单文件组件:

以.vue结尾的包含html css js 的一个页面的组件 ,默认自动导出

(插件 vue vscode snippets) vbase-css 自动生成.vue结构

组件名大写 Test

组件的样式是全局的

  • html 写在template标签里面,必须保证有一个根节点
  • js写在script标签里面
  • css写在style标签里,style里面可以添加一个scoped,代表该样式仅在当前组件内生效
  • render: h => h(App)的原理(接收一个组件,创造出来,挂载到div里面)
  • 组件嵌套

views文件夹写入组件

app.vue

导入组件(import xxx from "./views/xxx.vue")

注册组件(components:{xxx}

调用组件<xxx></xxx>

没有内容的标签可以自闭和标签<test/>

@代表src目录

路由:npm install vue-router

高内聚(相同的东西),低耦合(不同的东西)

1.vue引入插件

vue引入的所有插件都需要注册 Vue.use(插件名)

2.VueRouter实例化

new VueRouter({接受路由配置项当参数})

3.路由配置项:

就是规定路径和组件的对应关系子路由如果不加斜线,那么默认在上级路由上叠加,如果加了斜线,那么前面必须带上上级路由

注意:页面与路由一定是一一对应的,一个页面一定要对应路由,否则找不到 (地址-->视图)

4.路由懒加载:

提高性能 component: () => import('../views/xxx.vue')

6.使用路由的步骤

(1)定义路由组件 (单文件组件)

(2) 定义路由配置项(path,name,meta,component)

(3) 创建路由实例,传入配置项

(4) 挂载

7.路由的视图显示:

路由必须要有router-view 来规定路由的显示位置 <router-view/>

8.嵌套路由:

哪个组件还有子路由,需要在该组件内部定义router-view,代表该组件的子路由显示在该位置上。

9.路由的跳转

$router 是“路由实例”对象包括了路由的跳转方法,钩子函数等

通过标签跳转:

<router-link to="/about"></router-link> 字符串路径

<router-link :to="{path:"路径"}"></router-link> 对象路径

<router-link :to="{name:"名字"}"></router-link> 对象名字

编程式导航:

this.$router.push(字符串路径)

this.$router.push({path:"路径"})

this.$router.push({name:"名字"})

this.$router.replace(面试中问道)

10. Vue-router跳转和location.href有什么区别

●使用 location.href= /url 来跳转,简单方便,但是刷新了页面;

●使用 router.push( /url ) 来跳转,使用了 diff 算法,实现了按需加载,减少了 dom 的消耗。vue-router用了 history.pushState() 。

11.动态路由匹配(数据跨页面传递):

$route 是“路由信息对象”,

路由信息参数{name:"组件的名字",fullPath:"完整路径",path:"路径",matched:[匹配上的所有路由],meta:"路由元信息",params:"动态路径参数",query:"查询参数"}

在路径后面加/:名字

任一路径只匹配一个页面

注意:只改后面动态路径参数是不会重新触发组件生命周期的

this.id = this.$route.params.id;

12.query和params的区别

注意:query刷新不会丢失query里面的数据 params刷新会丢失 params里面的数据。

//params传参,使用name跳转  post
this.$router.push({
    name:'second',
    params: {
        queryId:'20180822',
        queryName: 'query'
    }
})

//query传参,使用path跳转   get
this.$router.push({
    path:'second',
    query: {
        queryId:'20180822',
        queryName: 'query'
    }
})
//query传参接受
this.queryName = this.$route.query.queryName;
this.queryId = this.$route.params.queryId;

13.路由的重定向和别名(路径隐藏):

1.重定向的意思是,当用户访问/Home时,URL 将会被替换成/Test,然后匹配路由为 /b

const routes = [   
  {
    path: '/Home', 
    redirect: '/Test',   //重定向
    name: 'Home',
    components: {a:Home,b:About},  // 命名路由
  },

redirect:"要跳转的路径"

2、别名

/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样

const router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
  ]
})

14、路由模式

1. hash模式

简介: hash模式是开发中默认的模式,它的URL带着一个#,例如:http://www.abc.com/#/vue,它的hash值就是#/vue

特点:hash值会出现在URL里面,但是不会出现在HTTP请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。。

2. history模式

简介: history模式的URL中没有#,它使用的是传统的路由分发模式,即用户在输入一个URL时,服务器会接收这个请求,并解析这个URL,然后做出相应的逻辑处理。

特点: 当使用history模式时,URL就像这样:http://abc.com/user/id。相比hash模式更加好看。但是,history模式需要后台配置支持。如果后台没有正确配置,访问时会返回404。

如果想要切换到history模式,就要进行以下配置(后端nginx配置转发):

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

15. 如何获取页面的hash变化

(1)监听$route的变化

// 监听,当路由发生变化的时候执行
watch: {
  $route: {
    handler: function(val, oldVal){
      console.log(val);
    },
    // 深度观察监听
    deep: true
  }
},

(2)window.location.hash读取#值

window.location.hash 的值可读可写,读取来判断状态是否改变,写入时可以在不重载网页的前提下,添加一条历史访问记录。

16.路由优先级

path:"*",匹配404页面,放在最后一个路由

17.导航守卫

全局前置守卫

const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
  // ...
})

每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标 路由对象
  • from: Route: 当前导航正要离开的路由
  • next: Function: 一定要调用该方法来resolve这个钩子。执行效果依赖next方法的调用参
  • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
  • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
  • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。

确保 next 函数在任何给定的导航守卫中都被严格调用一次;

//死循环
router.beforeEach((to,from,next)=>{
  next("/Test")
})


router.beforeEach((to,from,next)=>{
  if(to.path=="/Test"){
    next()
  }
  next("/Test")
})

组件内的守卫:

const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建

    next(vm => {
        // 通过 `vm` 访问当前组件实例
      })
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

axios:Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

使用说明 · Axios 中文说明 · 看云

npm i axios --save-dev

关于插件的注册:

1.如果是给vue量身定制的,要通过Vue.use(插件名)去注册 vue-router vuex

2.如果不是给vue量身定制的,通过挂载到原型上去注册 axios echarts

//main.js  继承  根实例加$
import axios from 'axios'
Vue.prototype.$axios=axios   //axios挂载到原型上

get/post请求

//get请求
$axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(response=>{
    console.log(response);
  })
  .catch(error=>{
    console.log(error);
  });
//post请求
$axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })

自定义请求

var instance = axios.create({
// `url` 是用于请求的服务器 URL
  url: '/user',

  // `method` 是创建请求时使用的方法
  method: 'get', // 默认是 get

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: 'https://some-domain.com/api/',
  // `headers` 是即将被发送的自定义请求头
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` 是即将与请求一起发送的 URL 参数
  // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
  params: {
    ID: 12345
  },
   // `data` 是作为请求主体被发送的数据
  // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
  // 在没有设置 `transformRequest` 时,必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属:FormData, File, Blob
  // - Node 专属: Stream
  data: {
    firstName: 'Fred'
  },
   // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
  // 如果请求话费了超过 `timeout` 的时间,请求将被中断
  timeout: 1000,
});

拦截器(拦截器必须在get请求之前)

// 添加请求拦截器   
instance.interceptors.request.use(config=>{
    //全局的  1、带参数  3、请求验证参数
    config.params.token="111"
    return config;
  }, error=>{
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(response => {
    //判断请求是否成功
    return response;
  }, error=>{
    // 对响应错误做点什么
    return Promise.reject(error);
  });

vue.config.js配置跨域(与src目录平行)

 module.exports = {
      devServer: {
        proxy: {
          '/api': {
            /* 目标代理服务器地址 */
            target: 'https://api.binstd.com/recipe/search',
            /* 允许跨域 */
            changeOrigin: true,
            pathRewrite: {
              "^/api": ""
            }
          },
        },
      },
    }

服务端与客户端(跨域)

请求代理(本地服务器代理)

vue中如何实现跨域访问

1.开发环境:配置vue.config.js proxy代理
2.生产环境:配置nginx代理

Vuex 的原理

开始 | Vuex

Vuex 实现了一个单向数据流

1.state:存放数据的 { }

访问数据:

<p>{{msg}}</p>

//取值的时候一定设置为当前组件的计算属性
computed:{
      msg(){
        return this.$store.state.msg
      }
    }

state: {
    msg:"我是vuex数据"
  },

2.getters:类似于计算属性 ,默认接受一个参数,该参数是所有的state,其他的用法跟computed是一模一样的 返回一个函数,可以传参

 <p>{{msg}}</p>

 computed:{
      msg(){
        return this.$store.getters.dollar
      }
    }

 getters:{
    dollar(state){
      return state.msg+"你好啊"
    }
  },
    
 传参(返回一个函数):
 <p>{{msg}}</p>
 computed:{
      msg(){
        return this.$store.getters.dollar("参数")
      }
    }
getters:{
    dollar(state){
      return m=>state.msg+m
    }
  },

3.Vuex 的状态存储是响应式的,改变 store 中的状态的唯一途径就是显式地提交 mutation。

mutation:类似于methods,默认两个参数,第一个为state,第二个为payload,就是你想做的事情的列表,mutation要通过commit提交

当然commit方法还可以传入额外的参数,官方给这个额外的参数起了一个高大上的名字,叫载荷(payload),一般情况下,我们很少直接传一个number或者string当参数,大部分情况下其实都是传一个对象的。

mutation必须是同步函数,否则无法追踪数据变化

<p>{{msg}}</p>
<p @click="change()">改变</p>

methods:{
    change(){
        this.$store.commit({type:"add"})
    }
}
mutations: {
    add(state){
        return state.msg=20
    }
},
    
传参:
<p>{{msg}}</p>
<p @click="change()">改变</p>

methods:{
    change(){
        this.$store.commit({type:"add",num:20})
    }
}

mutations: {
    add(state,payload){
        return state.msg=state.msg+payload.num
    }
},

4.action

默认接受一个参数,store实例

action提交的是mutation,而不是直接变更状态;

action可以包含任意异步操作

<p>{{msg}}</p>
<p @click="change()">改变</p>

methods:{
    change(){
        this.$store.dispatch("changeData",10)
    }
}

actions: {
    changeData({commit},n){
        axios.get().then(res=>commit('add',n))
    }
},

5.mapState mapActions mapMutations mapGetters

import { mapState,mapMucations } from 'vuex'  //辅助函数
export default ({
computed: { 
    ...mapState(['aa', 'bb']),
    ...mapGetters(['a','b'])
} 
methods:{
  ...mapMucations(['add']),
  ...mapActions(['add']),
  change(){
        this.add()
  }
}
})

6.module

分模块之后只有state是局部的(namespaced:true,//开启局部空间)

创建模块文件:store-->modules文件夹-->(moduleA.js;moduleB.js)

index.js:

导入模块文件:import moduleA from "./modules/moduleA.js"

注册模块文件:modules: {moduleA,moduleB}

访问模块文件:state:...mapState("moduleA",['cc'])(其余同理)

3. Vuex 和 localStorage 的区别

(1)最重要的区别

  • vuex存储在内存中
  • localstorage 则以文件的方式存储在本地,只能存储字符串类型的数据,存储对象需要 JSON的stringify和parse方法进行处理。
  • 读取内存比读取硬盘速度要快

(2)应用场景

  • vuex用于组件之间的传值。
  • localstorage是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用 。
  • Vuex能做到数据的响应式,localstorage不能

(3)永久性

刷新页面时vuex存储的值会丢失,localstorage不会。

vuex(临时存储) localhost(持久化存储)

解决vuex临时存储:vuex-persistedstate插件(面试中问道)

插件的原理其实也是结合了存储方式,只是统一的配置就不需要手动每次都写存储方法

使用方法

  • 安装npm install vuex-persistedstate --save
  • 引入及配置    在store下的index.js中  存储在sessionstory里

import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
  // ...
  plugins: [createPersistedState({
      storage: window.sessionStorage
  })]
})

7. Vuex和单纯的全局对象有什么区别?

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  • 不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交mutation。这样可以方便地跟踪每一个状态的变化

9. Vuex的严格模式是什么,有什么作用,如何开启?

在严格模式下,无论何时发生了状态变更且不是由mutation函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。

在Vuex.Store 构造器选项中开启,如下

const store = new Vuex.Store({
    strict:true,
})

for循环后computed渲染:

 computed: {
    stars: function () {
      return function (star) {
        var count = 0
        star.forEach((item, index) => {
          if (item) {
            count++
          }
        })
        return count
      }
    }
  }
Logo

前往低代码交流专区

更多推荐