vue2.0 笔记

VSCode 关于vue的扩展安装: Vue VSCode Snippets 、Vetur

    <div id="app">{{title}}</div>

    <script src="./vue.js"></script>

    <script>
        const app = new Vue({
            el: '#app',
            data() {
                return {
                    title: 'hello,vue'
                }
            }
        })
        setTimeout(() => {
            app.title = 'mua~,vue'
        },1000);
    </script>

理解vue的设计思想

        1、数据驱动应用

        2、MVVM模式的践行者

MVVM框架的三要素:响应式、模板引擎及其渲染

        响应式:vue如何监听数据变化?

        模板:vue的模板如何编译和解析

        渲染:vue如何将模板转换为html

核心知识03--模板语法

        vue模板语法

                vue.js使用了基于HTML的模板语法,允许开发者声明式的将DOM绑定到底层Vue实例的数据,所有Vue.js的模板都是合法的HTML,所以能被遵循规范的浏览器和HTML解析器解析。 

插值文本 

        数据绑定最常见的形式就是使用"Mustache"语法(双大括号)的文本插值

范例:设置标题

     

    <div id="app">
        <h2>
            <!-- 插值文本 -->
            {{title}}
        </h2>
    </div>

    <script src="./vue.js"></script>

    <script>
        const app = new Vue({
            el: '#app',
            data() {
                return {
                    title: 'hello,vue'
                }
            }
        })
    </script>

特性

        HTML特性不能使用"Mustache"语法,应该使用v-bind指令

范例-设置标题

    <div id="app">
        <!-- 特性、属性值绑定使用v-bind指令-->
        <h2 v-blind:title="title">...</h2>
    </div>

列表渲染

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

范例:课程列表

    <div id="app">
        <!-- 条件渲染 -->
        <p v-if="courses.length == 0">没有任何课程信息</p>
        <!-- 列表渲染 -->
        <ul>
            <li v-for="c in courses" :key="c">
                {{ c }}
            </div>
        </ul>
    </div>

    <script src="./vue.js"></script>

    <script>
        const app = new Vue({
            el: '#app',
            data() {
                return {
                    courses: ['web全栈','web高级']
                }
            }
        })
    </script>

表单输入绑定

你可以用 v-modal 指令在表单 <input>、<textarea>、及 <select> 元素上创建双向数据绑定。它会根据控件类型自动选择正确的方法来更新元素。 v-modal 本质上是语法糖。它将转换为输入事件以更新数据,并对一些极端场景进行一些特殊处理。

范例:新增课程

   <!-- 表单输入绑定 -->
   <input v-model="course" type="text" v-on:keydown.enter="addCourse"/>

事件处理

可以用 v-on 指令监听DOM事件,并在触发时运行一些JavaScript代码

范例:新增课程

    <!-- 事件处理 -->
    <button v-on:click="addCourse">新增课程</button>

    <script>
        const app = new Vue({
            el: '#app',
            data() {
                return {
                    course:'',
                    courses: ['web全栈','web高级']
                }
            },
            methods: {
                addCourse() {
                    this.courses.push(this.course)
                    this.course = ''
                }
            },
        })
    </script>

class与style绑定

操作元素的 class 列表和内联样式是数据绑定的一个常见需求,因为他们都是属性,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。

        <style>
            .active{
                background-color: #ddd;
            }
        </style>

        <ul>
            <!-- class绑定 -->
            <li 
                v-for="c in courses" 
                :key="c" 
                :class="{active: (selectedCourse === c)}" 
                @click="selectedCourse = c">
                     {{ c }} 
            </li>
            <!-- style绑定 -->
            <li 
                v-for="c in courses" 
                :key="c" 
                :style="{backgroundColor: (selectedCourse === c)? '#ddd' : 'transparent'}" 
                @click="selectedCourse = c">
                     {{ c }} 
            </li>
        </ul>

   <script>
        const app = new Vue({
            el: '#app',
            data() {
                return {
                    // 保存选中项
                    selectedCourse:''
                }
            },
        })
    </script>

计算属性和监听器

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,此时就应该考虑计算属性和监听器。

范例:课程数量统计

        <p>
            <!-- 绑定表达式 -->
            <!-- 课程总数:{{courses.length + '门'}} -->
            <!-- 计算属性 -->
            <!-- 课程总数:{{total}} -->
            <!-- 监听器 -->
            课程总数:{{totalCount}}
        </p>
        
        <script>
            const app = new Vue({
                el: '#app',
                data() {
                    return {
                        totalCount:''
                    }
                },
                compute: {
                    total () {
                        return this.courses.length + '门'
                    }
                },
                watch: {
                    // 下面这种不会生效,因为初始化时不会触发
                    courses(newValue, oldValue) {
                        this.totalCount = newValue.length + '门'
                    }  
                },
                watch: {
                    // 这种方式会在初始化时触发
                    courses: {
                        immediate: true,
                        deep: true,
                        handler(newValue, oldValue) {
                            this.totalCount = newValue.length + '门'
                        }
                    }
                },
            })
        </script>

计算属性 VS 监听器

        1、监听器更通用,理论上计算属性能实现的监听器也能实现

        2、处理数据的场景不同,监听器适合一个数据影响多个数据,计算属性适合一个数据受多个数据影响

        3、计算属性有缓存行,计算所得的值如果没有变化不会重复执行

        4、监听器适合执行异步操作或比较大开销操作的情况

神奇的模板语法是如何实现的

在底层的实现上,Vue将模板编译成虚拟DOM渲染函数,结合响应系统,Vue能够智能地计算出最少需要重新渲染多少组件,并把DOM操作次数减到最少。

// 输出vue替我们生成的渲染函数一窥究竟
console.log(app.$options.render)
//它长这个样子
(functionanonymous(
){
with(this){return_c('div',{attrs:{"id":"app"}},[_c('h2',{attrs:
{"title":title}},[_v("\n"+_s(title)+"\n")]),_v("
"),_c('input',{directives:[{name:"model",rawName:"v-model",value:
(course),expression:"course"}],attrs:{"type":"text"},domProps:{"value":
(course)},on:{"keydown":function($event)
{if(!$event.type.indexOf('key')&&_k($event.keyCode,"enter",13,$event.key,"Enter"
))returnnull;returnaddCourse($event)},"input":function($event)
{if($event.target.composing)return;course=$event.target.value}}}),_v("
"),_c('button',{on:{"click":addCourse}},[_v("新增课程")]),_v(""),(courses.length
==0)?_c('p',[_v("没有任何课程信息")]):_e(),_v("
"),_c('ul',_l((courses),function(c){return_c('li',{class:{active:
(selectedCourse===c)},on:{"click":function($event){selectedCourse=c}}},
[_v(_s(c))])}),0)])}
})

结论:Vue通过它的编译器将模版编译成渲染函数,在数据发生变化时的时候再次执行渲染函数,通过对比两次执行结果得出要做的dom操作,模版中的魔法得以实现。

生命周期

每个Vue实例在创的时候都要经过一系列的初始化过程--例如,需要设置数据监听, 编译模板, 将实例挂载到DOM并在数据变化时更新DOM等, 称为Vue示例的生命周期

使用生命周期钩子

在Vue实例的生命周期过程中会运行一些叫做生命周期钩子的函数, 这给用户在不同阶段添加自己代码的机会.

范例: 异步获取课程列表

    <script>
        // 模拟异步数据调用接口
        function getCourses(){
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(['web全栈','web高级'])
                }, timeout);
            })
        }

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    courses:[],
                }
            },
            // created钩子中调用接口
            async created () {
                const courses = await getCourses();
                this.courses = courses;
            }
        })
    </script>

生命周期结论:

        三个阶段: 初始化  更新  销毁

        初始化: beforeCreate  created  beforeMount  mounted

        更新:  beforeUpdate  updated

        销毁:  beforeDestroy  destroyed

使用场景分析:

        {

                beforeCreate(){}  // 执行时组件实例还未创建, 通常用于插件开发中执行一些初始化任务

                created(){}         // 组件初始化完毕, 各种数据可以使用, 通常用于异步数据获取

                beforeMount(){}    // 未执行渲染, 更新, dom未创建

                mounted(){}         // 初始化结束, dom已创建, 可用于获取访问数据和dom元素

                beforeUpdate(){}    // 更新前, 用于获取更新前各种状态

                updated(){}        // 更新后, 所有状态已是最新

                beforeDestroy(){}     // 销毁前,  可用于一些定时器或订阅的取消

                destroyed(){}        // 组件已销毁,作用同上

        }

组件化

组件是可复用的Vue实例,带有一个名字,我们可以在一个通过new Vue 创建的Vue根实例中,把这个组件作为自定义元素来使用.

组件注册、使用以及数据传递

Vue.component(name, options)  // 可用于组件注册

范例: 提取课程新增和课程列表组件

    <!-- 列表渲染 -->
    <course-list :courses="courses"></course-list>

    <script>
        // 注册course-list组件
        Vue.component('course-list',{
            data() {
                return {
                    selectedCourse: ''
                }
            },
            props: {
                courses: {
                    type: Array,
                    default: [] 
                },
            },
            template: `
                <div>
                    <!-- 条件渲染 -->
                    <p v-if="courses.length === 0">没有任何课程信息</p>
                    <!-- 列表渲染 -->
                    <ul>
                        <!-- class绑定 -->
                        <li 
                            v-for="c in courses" 
                            :key="c" 
                            :class="{active: (selectedCourse === c)}" 
                            @click="selectedCourse = c">
                                {{ c }} 
                        </li>
                    </ul>
                </div>
            `
        })

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    courses: ['web全栈','web高级'],
                }
            }
        })
    </script>

自定义事件及其监听

当子组件需要和父组件进行通信,  可以派发并监听自定义事件

范例: 新增课程组件派发新增事件

        <!-- 课程添加组件调用 -->
        <course-add @add-course="addCourse"></course-add>

        // 注册course-add组件
        Vue.component('course-add', {
            data() {
                return {
                    course: '', // 将course从父组件提取到course-add维护
                }
            },
            template: `
                <div>
                    <!-- 表单输入绑定 --> 
                    <input v-model="course" @keydown.enter="addCourse"/>   
                    <!-- 事件处理 -->
                    <button v-on:click="addCourse">新增课程</button>
                </div>
            `,
            methods: {
                // 回调函数接收事件参数
                addCourse() {
                    // 发送自定义事件通知父组件
                    // 注意事件名称定义时不要有大写字母出现
                    this.$emit('add-course', this.course )
                    this.course = ''
                }
            }
        })
        const app = new Vue({
            el: '#app',
            data() {
                return {
                    courses: ['web全栈','web高级'],
                }
            },
            methods: {
                addCourse(course) {
                    this.courses.push(course)
                }
            }
        })

在组件上使用v-modal

范例: 改造course-add为支持v-modal的版本

        <!-- 自定义组件支持v-modal需要实现内部的input的:value和@input  -->
        <course-add v-modal="course" @add-course="addCourse"></course-add>

     <script>
        // 注册course-add组件
        Vue.component('course-add', {
            props:['value'],
            template: `
                <div>
                    <!-- 需要实现input的:value和@input --> 
                    <input :value="value" @input="onInput" @keydown.enter="addCourse"/>   
                    <!-- 事件处理 -->
                    <button v-on:click="addCourse">新增课程</button>
                </div>
            `,
            methods: {
                addCourse() {
                    // 派发事件不再选哟传递数据
                    this.$emit('add-course', this.course )
                },
                onInput (e) {
                    this.$emit('input', e.target.value)
                }
            },
        })
       

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    course:'',  // 还原course
                    // 保存选中项
                    selectedCourse:'',
                    courses: ['web全栈','web高级'],
                    totalCount:''
                }
            },
            methods: {
                addCourse() {
                    this.courses.push(this.course)
                }
            },
        })
    </script>

v-model 默认转换是:value和@input, 如果想要修改这个行为, 可以通过定义model选项

Vue.component('course-add', {
    model: {
        prop:'value',
        event: 'change'
    }
})

通过插槽分发内容

        通过使用vue提供的 <slot> 元素可以给组件传递内容

范例:弹窗组件

    <style>
        .message-box{
            padding: 10px 20px;
            background: #4fc08d;
            border: 1px solid #42b983;
        }
        .message-box-close {
            float: right;
        }
    </style>

    <!-- 插槽实现内容分发 -->
    <message :show.sync="show" @update-show="updateShow">新增课程成功!</message>

    <script>
        // 注册message组件 slot做为占位符
        Vue.component('message', {
            props: ['show'],
            template: `
                <div class="message-box" v-if="show">
                    <slot></slot>
                    <span class="message-box-close" @click="$emit('update-show',     false)">X</spam>
                </div>
            `

        })

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    show: false, // 提示框状态
                    course:'',  // 还原course
                    // 保存选中项
                    selectedCourse:'',
                    courses: ['web全栈','web高级'],
                    totalCount:''
                }
            },
            methods: {
                // 还原addCourse
                addCourse() {
                    this.courses.push(this.course)
                    // 提示框新增信息
                    this.show = true
                },
                updateShow (bool) {
                    this.show = bool;
                }
            },
        })
    </script>

如果存在多个独立内容要分发,可以使用具名插槽v-slot:name

范例: 添加一个title部分

        <!-- 插槽实现内容分发 -->
        <message :show.sync="show" @update-show="updateShow">
            <template v-slot:title>恭喜</template>
            <template>新增课程成功!</template>
        </message>

        // 注册message组件 slot做为占位符
        Vue.component('message', {
            props: ['show'],
            template: `
                <div class="message-box" v-if="show">
                    <strong><slot name="title"></slot/></strong>
                    <slot></slot>
                    <span class="message-box-close" @click="$emit('update-show', false)">X</spam>
                </div>
            `
        })

Vue组件化的理解3

组件化是Vue的精髓,Vu而英勇就是由一个个组件构成的, Vue的组件化设计到的内容非常多, 可以从一下几点进行阐述.

定义: 组件是可复用的Vue实例, 准确讲它们是VueComponent的实例, 继承自Vue

优点: 从上面案例可以看出组件化可以增加代码的复用性、 可维护性、和可测试行

使用场景: 什么时候使用组件?以下分类可作为参考

        1、通用组件: 实现最基本的功能, 具有通用性  复用性, 例如按钮组件、输入框组件、布局组件等

        2、业务组件: 它们完成具体业务, 具有一定的复用性, 例如登陆组件, 轮播图组件

        3 、页面组件: 组织应用各部分独立内容, 需要时在不同页面组件间切换, 例如列表页, 详情页组件

如何使用组件

        1、 定义: Vue.component() 、 comonents选项、sfc

        2、分类: 有状态组件, functional 、 abstract

        3、 通信 props、$emit() / $on()、 provide/inject、 $children / $parent/、 $root、 $attrs、 $listeners

        4、内容分发: <slot>, <template>, v-slot

        5、 使用及优化: is, keep-alive, 异步组件

组件的本质

        vue中的组件经理如下过程

        组件配置 => Vu二Component实例 => render() => Virtual DOM => DOM

拓展知识--可复用性

过滤器

Vue.js允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和v-bind表达式。过滤器应该被添加在JavaScript表达式的尾部,由"管道"符号指示

<!-- 双花括号中 -->
{{ message | capitalize }}

<!-- v-bind 中-->
<div v-bind:id="rawId | formatId"></div>

范例:货币符号使用

    <div id="app">
        <h2>Web 开发工程师最低薪资是 {{ number | currency('¥') }}</h2>
    </div>

    <script>
        const app = new Vue({
            el: '#app',
            data() {
                return {
                    number: 10000
                }
            },
            filters: {
                currency (value, symbol = '$') {
                    return symbol + value;
                }
            }
        })
    </script>

自定义指令

除了核心功能默认内置的指令(v-modal 和 v-show),Vue也允许自定义指令。注意,在Vue2.0中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然要对普通DOM元素进行底层操作,这时候就会用到自定义指令

范例:输入框获取焦点

directives/focus.js

export default {
    directiveName: 'focus',
    options: {
        inserted(el) {
            el.focus();
        }
    }
}

directives/permision.js

export default {
    directiveName: 'permision',
    options: {
        inserted(el,binding) {
            if (binding.value !== 'admin') {
                el.parentElement.removeChild(el);
            }
        }
    }
}

directives/index.js

import focus from './focus';
import permision from './permision';

export default {
    focus,
    permision
}

 main.js

import Vue from 'vue'
import App from './App.vue'
// 自定义指令
import directives from './directives'

Vue.config.productionTip = false
Vue.prototype.$bus = new Vue()

const { focus, permision } = directives

// 获取焦点自定义指令
Vue.directive(focus.directiveName, focus.options)
// 权限自定义指令
Vue.directive(permision.directiveName, permision.options)

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

 CourseAdd.vue

 <!-- 表单输入绑定 -->
 <input v-model="course" type="text" v-on:keydown.enter="addCourse" @input="onInput" v-focus/>

 DirectiveBtn.vue

<template>
    <div v-permision="'user'">
        <button>自定义指令按钮</button>
    </div>
</template>

 

 

Vue.set 、Vue.delete 响应式数据添加删除

Vue.set: 向响应式数据中添加一个属性,并确保这个新的响应式数据同样是相应式的,且触发视图更新。使用方法:Vue.set(target, propertyName/index, value)

范例:设置商品价格

<template>
    <div>
        <!-- 添加批量加格更新 -->
        <p>
            <input type="text" v-model.number="price">
            <button @click="batchUpdate">批量更新价格</button>
        </p>
        <ul>
            <!-- class绑定 -->
            <li 
                v-for="c in courses" 
                :key="c" 
                :class="{active: (selectedCourse === c)}" 
                @click="selectedCourse = c">
                     {{ c.name }} - ¥{{c.price || 0}} 
            </li>
            <!-- style绑定 -->
            <!-- <li 
                v-for="c in courses" 
                :key="c" 
                :style="{backgroundColor: (selectedCourse === c)? '#ddd' : 'transparent'}" 
                @click="selectedCourse = c">
                     {{ c }} 
            </li> -->
        </ul>
    </div>
</template>

<script>
    export default {
        props: {
            courses: {
                type: Array,
                required: true,
                default: function () {
                    return []
                }
            },
        },
        data() {
            return {
                // 保存选中项
                selectedCourse:'',
                price: 0    // 增加加格数据
            }
        },
        methods:{
            batchUpdate(){
                this.courses.forEach(course => {
                    this.$set(course, 'price', this.price)
                })
            }
        }
    }
</script>

<style scoped>
    .active{
        background-color: #ddd;
    }
</style>

Vue.delete

删除对象的属性。如果对象是响应式的,确保删除能触发更新视图

使用方法:Vue.delete(target, property/index)

Vue事件总线 $bus

通过在Vue原型上添加一个Vue实例作为事件总线,实现组件之间相互通信,而且不受组件之间关系的影响

Vue.prototype.$bus = new Vue()

范例:关闭消息框

Message.vue

<template>
    <div class="message-box" v-if="show">
        <strong><slot name="title"></slot></strong>
        <slot></slot>
        <span class="message-box-close" @click="$emit('update-show', false)">X</span>
    </div>
</template>

<script>
    export default {
        props: ['show'],
        mounted () {
            this.$bus.$on('message-close', () => {
                this.$emit('update-show', false)
            });
        },
    }
</script>

<style scoped>
    .message-box{
        padding: 10px 20px;
        background: #4fc08d;
        border: 1px solid #42b983;
    }
    .message-box-close {
        float: right;
        cursor: pointer;
    }
</style>

 ClearMessage.vue

<template>
    <button class="toolbar" @click="clearMessage">
        清空提示框
    </button>
</template>

<script>
    export default {
        name: 'ClearMessage',
        methods:{
            clearMessage(){
                // 派发关闭事件
                this.$bus.$emit('message-close', false)
            }
        },
        
    }
</script>

<style lang="scss" scoped>

</style>

 main.js

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

Vue.config.productionTip = false
Vue.prototype.$bus = new Vue()

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

vm.$once

监听一个自定义事件,但是只触发一次。一旦出发之后,监听器就会被移除。

vm.$once('test', function(msg) {
    console.log(msg)
})

vm.$off

移除自定义事件监听器

        1、如果没有提供参数,则移除所有的事件监听器

        2、如果只提供了事件,则移除该是按所有的监听器

        3、如果同时提供了事件与回调,则只移除这个回调的监听器

vm.$off()    // 移除所有的事件监听器
vm.$off('test')    // 移除该事件所有的监听器
vm.$off('test', callback)    // 只移除这个回调的监听器

ref、vm.

$refs

组件或元素引用:ref和vm.$refs, ref被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的$refs对象上。如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向该子组件

<input type="text" ... ref="inp"> 

mounted(){ 
    // mounted之后才能访问到inp 
    this.$refs.inp.focus() 
}

 范例:改造message组件用打开、关闭方法控制显示

<!--自定义组件引用--> 
<message ref="msg">新增课程成功!</message> 

<script> 
    Vue.component('message', { 
        // 组件显示状态 
        data() { 
            return { 
                show: false 
            } 
        },
        template: ` <div class="message-box" v-if="show"> 
                        <slot></slot> 
                        <!--toggle内部修改显示状态--> 
                        <span class="message-box-close" @click="toggle">X</span> 
                    </div> `,
        // 增加toggle方法维护toggle状态 
        methods: { 
            toggle() { 
                this.show = !this.show; 
            } 
        },
        // 修改message-close回调为toggle 
        mounted () { 
            this.$bus.$on('message-close', this.toggle); 
        }, 
    })
    const app = new Vue({
         methods: { 
            addCourse() { 
                // 使用$refs.msg访问自定义组件 
                this.$refs.msg.toggle() 
            } 
        } 
    })
</script>

 注意:

        1、ref是作为渲染结果被创建的,在初始渲染时不能访问它们

        2、$refs不是响应式的,不要试图用它在模板中做数据绑定

        3、当v-for用于元素或组件时,引用信息将是包含DOM节点或组件实例的数组

Vue-cli

快速原型开发----可以使用 vue server 和 vue build 命令对单个.vue文件进行快速原型开发

安装: 

npm install -g @vue/cli-service-global

准备一个原型内容---- vue serve ------ 启动一个服务并运行原型----- vue serve hello.vue

vue serve hello.vue

要求:vue-cli须在4.x

vuec-cli升级 

npm uninstall -g vue-cli     // 卸载

npm install -g @vue/cli
 npm list -g --depth 0    // 查看npm全局安装模块详细信息

Vuex

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

安装

vue add vuex

1、state: 应用全局状态定义在state中

2、mutation:修改state只能通过mutation

3、action:类似于mutation,不同在于action提交的是mutation,而不是直接变更状态,action可以包含任意异步操作

4、getter:可以使用getters从store的state中派生出一些状态

5、插件:Vuex的store接受plugins选项,这个选项暴漏出每次mutation的钩子。Vuex插件就是一个函数,它接收store作为唯一参数

vuex相关文件:

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import focus from './directives/focus'
import store from './store'
import './plugins/element.js'

Vue.config.productionTip = false
Vue.prototype.$bus = new Vue()
Vue.directive('focus', focus)

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

/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import user from './user'
import persist from './plugins/persist'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    user
  },
  strict: true,
  plugins: [persist]
})

/store/user.js

import {addAuthRoute} from '@/router/index'

export default {
  namespaced: true, // 设置独立命名空间,避免命名冲突
  state: {
    isLogin: false,
    username: ''
  },
  mutations: {
    login(state, username) {
      state.isLogin = true
      state.username = username
    },
    logout(state) {
      state.isLogin = false
      state.username = ''
    },
  },
  getters: {
    welcome: state => state.username + ',欢迎回来'
  },
  actions: {
    // 参数1是vuex传递的上下文context:{commit, dispatch, state}
    login({ commit }, username) {
      // 模拟登录api调用,1s钟以后如果用户名是admin则登录成功
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (username === 'admin') {
            commit('login', username)
            // 动态添加认证路由
            addAuthRoute()
            resolve()
          } else {
            reject()
          }
        }, 1000);
      })
    }
  }
}

/store/plugins/persist.js   实现登录状态持久化

export default store => {
  // store初始化的时候,将存储在localStoreage中的状态还原
  if (localStorage) {
    const user = JSON.parse(localStorage.getItem('user'))
    if (user) {
      store.commit('user/login', user.username)
    }
  }

  // 如果用户相关状态发生变化,自动存入localStoreage
  store.subscribe((mutation, state) => {
    // {type:'user/login'}
    // {type:'user/logout'}
    // {type:'cart/addCart'}
    if(mutation.type === 'user/login') {
      const user = JSON.stringify(state.user)
      localStorage.setItem('user', user)
    } else if (mutation.type === 'user/logout') {
      localStorage.removeItem('user')
    }
  })
}

mapState、mapMutations、mapActions、mapGetters使用

login.js

<template>
  <div>
    <button @click="login" v-if="!isLogin">登录</button>
    <button @click="logout" v-else>注销</button>
    <p>{{welcome}}</p>
  </div>
</template>

<script>

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

export default {
  methods: {
    login() {
      // window.isLogin = true;
      // 提交mutation变更状态
    //   this.$store.commit('user/login')
      // 派发动作,触发actions
      // this.$store.dispatch("user/login", "admin").then(() => {
      this["user/login"]("admin").then(() => {
        this.$router.push(this.$route.query.redirect);
      }).catch(() => {
        alert('用户名或密码错误,请重试!')
      });
        this.LOGIN("admin")
    },
    logout() {
      // window.isLogin = false;
      this.$store.commit("user/logout");
      this.$router.push("/");
    },
    ...mapMutations('user',{LOGIN:'login',LOGOUT:'logout'}),
    ...mapActions(['user/login', 'user/logout'])
  },
  computed: {
    // isLogin() {
    //   return this.$store.state.user.isLogin;
    // }
    ...mapState('user', ['isLogin']),
    ...mapGetters('user',['welcome'])
  }
};
</script>

<style lang="scss" scoped>
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值