vue行走之路

作者: 苏阿柒 | 来源:发表于2018-08-08 16:59 被阅读1448次

        因为项目需要,所以需要学习,虽然理解不是很深,但是还是简单的记录一下。
        vue是一个前端开发框架,是一个mvvm模式框架,侧重于vm(视图模型)部分,同时具备了angular和react的优点,又很轻量,所以经常用在一些项目开发中。
        借用官方的一句话来说就是vue是一套构建用户界面的渐进式框架,是一个构建数据驱动的 web 界面的库,也可以理解为是一个视图模板引擎,强调的是状态到界面的映射,数据发生变化时,界面也一起发生改变。
        渐进式框架,我比较倾向于理解为自由,vue它本身并不是一个全面的框架,而是专注于视图层,那么我们在最基本的声明式渲染(视图模板引擎)上,可以随时的按照开发需求去添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架,让我们可以按需单独的添加某个部分的功能,而不需要将所有都加载进来,使得开发更加自由。
        这就是vue的设计理念,渐进式,也是vue的使用方式。

    vue生命周期

        首先我们先来一张图来简单了解一下


    2399431513-59cb0ac1e91b1_articlex.png

        从图中我们可以知道vue的生命周期大概是这样的 创建-监控-初始化-编译-替换-挂载-销毁
        我们先来简单的了解一下,在这个声明周期中vue做了哪些工作
    1、创建一个vue对象
    2、开始监控data对象数据变化
    3、vue内部初始化事件
    4、对模板进行编译,把这些模板编译成一个渲染函数Render,然后render函数调用createElement方法创建并且返回一个虚拟DOM树
    5、通过patch函数去进行虚拟DOM树和真实DOM的比对,从而将虚拟DOM上的变化添加到真实DOM上
    6、监控数据变化,实时更新DOM
    7、销毁组件上所有的监听,引用等
    8、销毁组件

        上面讲到,vue会对数据源进行监控,并实时更新到DOM,这个涉及到了双向数据绑定的原理,我们会在下面讲到。

        vue的生命周期过程中还涉及到了众多生命周期钩子方法,由于版本不同,我们这边也引用一张图来显示

    3346068135-580822cd52898_articlex.png
        从上图可以看出,2.0对比于1.0来说基本上的钩子方法都差不多,比1.0多出了beforeUpdate、updated、activated、deactivated这四个钩子方法
        这里就不一一列举各个钩子方法的具体使用方式了,大家有兴趣可以到官网进行了解https://cn.vuejs.org/v2/api/
    双向数据绑定原理以及实现
    1、原理

        vue的双向数据绑定主要是依靠于Object.defineProperty(),我们来看看MDN上对这个方法的定义

    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
    该方法允许精确添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in 或 Object.keys 方法), 这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改的。

        Object.defineProperty(obj, prop, descriptor)这个方法有3个参数,分别是obj(定义属性的对象)、prop(定义或者修改的属性的名称)、descriptor(被定义或者修改的属性描述符)
    obj跟prop都是字面上的意思,下面需要讲的是descriptor这个参数
        descriptor有两个主要的形式:数据描述符和存取描述符。

    数据描述符和存取描述符均具有以下可选键值:
        configurable
        当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false
        enumerable
    当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false

    数据描述符同时具有以下可选键值:
        value
        该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为undefined
        writable
        当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false

    存取描述符同时具有以下可选键值:
        get
        一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。默认为undefined
        set
        一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为undefined

        要注意的一点是:在 descriptor 中不能同时设置访问器(get 和 set)和 wriable 或 value,否则会错,就是说想用 get 和 set,就不能用 writable 或 value 中的任何一个。

    2、实现

        descriptor 的其他属性我们可以不必太关注,我们主要关注的重点是set和get这两个方法,我们来看一个简单的实例:

    <body>
        <div id="app">
            <input type="text" id="ipt">
            <p id="show-text"></p>
        </div>
        <script>
            var obj = {}
            var ipt = document.getElementById('ipt')
            var showText = document.getElementById('show-text')
            Object.defineProperty(obj, 'text', {
                get: function () {
                    return obj
                },
                set: function (newValue) {
                    ipt.value = newValue
                    showText.innerHTML = newValue
                }
            })
            ipt.addEventListener('keyup', function (e) {
                obj.text = e.target.value
            })
        </script>
      </body>
    

        在这个实例里,我可以随时获取输入框中的值,并在set函数中将这个值设置到obj对象中,包括我要获取所要显示的值,也是去obj对象中拿到并显示在视图中,由数据来驱动视图的更新,这个是不是很熟悉,这个就是vue的双向数据绑定了。

    vue组件

        vue组件是吧UI结构映射到恰当的组件树


    20170215102019940.png

    一个组件的创建基本上如下所示:

    Vue.component('button-counter', {
      data: function () {
        return {
          count: 0
        }
      },
      template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
    })
    

        这里是在全局中定义了一个名为button-counter的组件,组件中的数据就是data所返回的对象中的数据,组件模板就是template。然后我们就可以在项目中各处去使用这个组件了,是的,各处,这表示你可以按照需求进行组件嵌套,这就是全局注册的特点。当然,你也可以在局部去注册组件,但是这里就不详细讲了。除了像这样注册组件之外,vue还可以利用构建工具去配置单文件组件,每一个文件里都有 template, script 和 style这三个功能块,使我们可以更加方便的去管理所有的组件。

    要点:组件间的父子通信(单向传递)
        父组件可以通过props去向子组件传值,而子组件没有办法直接去传值给父组件,子组件想要跟父组件通信,就需要通过$emit去派发一个自定义事件到父组件中。然后父组件去在子组件标签上监听自定义事件,从而达到子组件向父组件通信的目的,具体的实现可以去参考我的另一篇文章《Vue2.0中父子组件间通信》

        除了父子组件通信之外,还有兄弟组件的通信,不过兄弟组件的通信也是基于父子组件通信上实现的,通过A子传父,父再传B子,在A子传父的基础上,让父组件去使用B子的方法,从而实现A子改变B子的状态。

    vue状态管理

        我们在小型项目中可能还可以利用父子组件通信和兄弟组件通信去管理数据,但是在涉及到较多的数据交互的时候,再用那种办法显然就不合适了,vue也提出了一个状态管理的办法去对数据源进行管理,使得我们在多个组件中可以去使用同一个状态,并且只需要维护一份数据就可以。


    state.png

        重要的是,注意你不应该在 action 中 替换原始的状态对象 - 组件和 store 需要引用同一个共享对象,mutation 才能够被观察
        组件不允许直接修改属于 store 实例的 state,而应执行 action 来分发 (dispatch) 事件通知 store 去改变。
        如果一个一个的去实现的话,会不会很麻烦呢?所幸,我们有vuex,每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
        1、Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
        2、你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
    这就很方便了

        我们在store文件中通过Vue.use方法去使用了Vuex,然后new 了一个Vuex.Store对象,在里面声明了state、getters、mutations和actions,并将这个对象返回,然后在根实例中注入,然后就可以在各个组件中去使用了,我们可以在组件中引入mapMutations去设置store 中的state中对应的数据,也可以通过引入mapGetters去获取对应的数据,相关的代码如下:

    store/index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    import axios from 'axios'
    Vue.use(Vuex)
    const store = new Vuex.Store({
        state: {
            select: false
        },
        getters: {
            select: state => state.select
        },
        mutations: {
            setSelect(state, select){
                if (!state.select) {
                    state.select = select
                }
            }
        },
        actions: {
        }
    })
    export default store
    

    入口文件main.js

    import Vue from 'vue'
    import App from './App'
    import store from './store'
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      store,
      render: h => h(App)
    })
    

    在子组件A中设置select的状态
    A.vue

    import { mapMutations } from 'vuex'
     export default {
        name: ' A',
        methods: {
            changeSelected(select) {
              this.setSelect(select)
            }
        },
        ...mapMutations({
            'setSelect'
        })
    }
    

    在子组件B中去获取
    B.vue

    import { mapGetters } from 'vuex'
    export default {
        name: 'B',
        computed: {
          ...mapGetters([
            'select'
          ])
        },
        created() {
          console.log(this.select)
        }
      }
    

    参考文献


    https://blog.csdn.net/wmwmdtt/article/details/55095420
    https://segmentfault.com/a/1190000011962150
    https://segmentfault.com/a/1190000008010666

    相关文章

      网友评论

        本文标题:vue行走之路

        本文链接:https://www.haomeiwen.com/subject/qaqxbftx.html