Vue.js 和 Vuex

--  linjianghe, 2016.7.7

主要内容

  • Vue.js
  • Vuex
  • 实践

Vue.js

Created by Evan You

What is Vue.js

一个构建数据驱动的 web 界面的库

Vue.js is a library for building interactive web interfaces.

MVVM

核心1:响应的数据绑定

核心2:组件系统

<div id="app">
  <app-nav></app-nav>
  <app-view>
    <app-sidebar></app-sidebar>
    <app-content></app-content>
  </app-view>
</div>

Why Vue.js

没有好与不好,只有适合和不适合

  • jQuery
  • Q.js、Angular、Polymer
  • React

Vue.js VS jQuery:计数的例子

<p>result: <span id="result">0</span></p>

<button id="plusBtn">加1</button>
$(function () {
    $('#plusBtn').on('click', function () {
        var oldVal = parseInt($('#result').text());
        $('#result').text(oldVal + 1);
    });
});
<p>result: <span v-text="result"></span></p>

<button v-on:click="plusOne">加1</button>
new Vue({
    el: 'body',
    data: {
        result: 0
    },
    methods: {
        plusOne: function () {
            this.result++;
        }
    }
})

jQuery的写法

Vue.js的写法

Vue.js VS jQuery:计数的例子

<p>result: <span id="result">0</span></p>
<button id="plusBtn">加1</button>
<button id="reduceBtn">减1</button>
$(function () {
    $('#plusBtn').on('click', function () {
        var oldVal = parseInt($('#result').text());
        $('#result').text(oldVal + 1);
    });

    $('#reduceBtn').on('click', function () {
        var oldVal = parseInt($('#result').text());
        $('#result').text(oldVal - 1);
    });
});
<p>result: <span v-text="result"></span></p>
<button v-on:click="plusOne">加1</button>
<button v-on:click="reduceOne">减1</button>
new Vue({
    el: 'body',
    data: {
        result: 0
    },
    methods: {
        plusOne: function () {
            this.result++;
        },
        reduceOne: function () {
            this.result--;
        }
    }
})

jQuery的写法

Vue.js的写法

Vue.js VS jQuery:计数的例子

<p>result: <span id="result">0</span></p>
<p>操作按钮点击数: <span id="clickCount">0</span> 次,
    最终的结果是<span id="finalResult"></span></p>
<button id="plusBtn">加1</button>
<button id="reduceBtn">减1</button>
$(function () {
    $('#plusBtn').on('click', function () {
        var oldVal = parseInt($('#result').text());
        $('#result').text(oldVal + 1);

        var oldClickCount = parseInt($('#clickCount').text());
        $('#clickCount').text(oldClickCount + 1);

        $('#finalResult').text($('#result').text());
    });

    $('#reduceBtn').on('click', function () {
        var oldVal = parseInt($('#result').text());
        $('#result').text(oldVal - 1);

        var oldClickCount = parseInt($('#clickCount').text());
        $('#clickCount').text(oldClickCount + 1);

        $('#finalResult').text($('#result').text());
    });
});
<p>result: <span v-text="result"></span></p>
<p>操作按钮点击数: <span v-text="clickCount"></span> 次,
    最终的结果是<span v-text="result"></span></p>
<button v-on:click="plusOne">加1</button>
<button v-on:click="reduceOne">减1</button>
new Vue({
    el: 'body',
    data: {
        result: 0,
        clickCount: 0
    },
    methods: {
        plusOne: function () {
            this.result++;
            this.clickCount++;
        },
        reduceOne: function () {
            this.result--;
            this.clickCount++;
        }
    }
})

jQuery的写法

Vue.js的写法

Vue.js VS Q.js

  1. Vue.js 只支持IE9及以上,Q.js 支持IE6
  2. Vue.js 26K,Q.js 7K
  3. 文档、调试工具、功能、生态等的差距

Vue.js VS Angular

  1. 更轻量(简单、灵活)
  2. 指令和组件分得更清晰
  3. Angular双向绑定,Vue.js也支持,但默认为单向绑定
  4. 不使用脏检查,更好的性能

Vue.js VS Polymer

  1. Vue.js 的组件与 Polymer 中的自定义元素类似,但 Vue.js 兼容到IE9,Polymer 依赖最新的 Web 组件特性,不支持的浏览器中需polyfill
  2. 打包工具,Polymer 元素需要用专用工具 vulcanizer 打包

Vue.js VS React

  1. 真实DOM VS Virtual DOM
  2. .vue VS jsx
  3. 动画
  4. 学习成本
  5. 同构(Vue.js 2.0 才支持)
  6. 生态

Vue.js的一些探讨

  1. 简单例子
  2. 核心概念
  3. 实现原理

Simple Vue.js App

Template

ViewModel

End Result

Main Vue.js Concepts

  • Constructors
  • Components
  • Directives
  • Filters
  • Transitions

生命周期

Vue 实例在创建时有一系列初始化步骤——例如,它需要建立数据观察,编译模板,创建必要的数据绑定。在此过程中,它也将调用一些生命周期钩子,给自定义逻辑提供运行机会。

响应式原理

  • Object.defineProperty
 /**
   * Define a reactive property on an Object.
   *
   * @param {Object} obj
   * @param {String} key
   * @param {*} val
   */

  function defineReactive(obj, key, val) {
    var dep = new Dep();

    var property = Object.getOwnPropertyDescriptor(obj, key);
    if (property && property.configurable === false) {
      return;
    }

    // cater for pre-defined getter/setters
    var getter = property && property.get;
    var setter = property && property.set;

    var childOb = observe(val);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function reactiveGetter() {
        var value = getter ? getter.call(obj) : val;
        if (Dep.target) {
          dep.depend();
          if (childOb) {
            childOb.dep.depend();
          }
          if (isArray(value)) {
            for (var e, i = 0, l = value.length; i < l; i++) {
              e = value[i];
              e && e.__ob__ && e.__ob__.dep.depend();
            }
          }
        }
        return value;
      },
      set: function reactiveSetter(newVal) {
        var value = getter ? getter.call(obj) : val;
        if (newVal === value) {
          return;
        }
        if (setter) {
          setter.call(obj, newVal);
        } else {
          val = newVal;
        }
        childOb = observe(newVal);
        dep.notify();
      }
    });
  }

...

v-if

v-show

...

Vuex

Vuex 是一个专门为 Vue.js 应用所设计的集中式状态管理架构。它借鉴了 Flux 和 Redux 的设计思想,但简化了概念,并且采用了一种为能更好发挥 Vue.js 数据响应机制而专门设计的实现。

import Vuex from 'vuex'

const state = {
  count: 0
}

const mutations = {
  INCREMENT (state) {
    state.count++
  }
}

export default new Vuex.Store({
  state,
  mutations
})
import store from './store.js'

// 变更
store.dispatch('INCREMENT')

// 读取
console.log(store.state.count) // -> 1

store.js

app.js

最简单的场景

<template>
  <div>
    <Display></Display>
    <Increment></Increment>
  </div>
</template>

<script>
import Display from './Display.vue'
import Increment from './Increment.vue'

export default {
  components: {
    Display: Display,
    Increment: Increment
  }
}
</script>
<template>
  <div>
    <h3>Count is 0</h3>
  </div>
</template>

<script>
export default {}
</script>

App.vue,根组件

Display.vue,子组件

计数器的例子

<template>
  <div>
    <button>Increment +1</button>
  </div>
</template>

<script>
export default {}
</script>

Increment.vue,子组件

计数器的例子-没有Vuex

  • Increment 与 Display 彼此无法感知到彼此的存在,也无法相互传递消息。
  • App 将必须通过事件(events)与广播(broadcasts)与其他两个组件进行协调。
  • 而 App 作为二者之间的协调者,导致这些组件并没法被复用,被迫紧密耦合。调整应用的结构,则可能导致应用崩溃。

计数器的例子-使用Vuex

vuex/store.js

第一步,加入store

components/App.vue

import Vue from 'vue'
import Vuex from 'vuex'

// 告诉 vue “使用” vuex
Vue.use(Vuex)

// 创建一个对象来保存应用启动时的初始状态
const state = {
  // TODO: 放置初始状态
}

// 创建一个对象存储一系列我们接下来要写的 mutation 函数
const mutations = {
  // TODO: 放置我们的状态变更函数
}

// 整合初始状态和变更函数,我们就得到了我们所需的 store
// 至此,这个 store 就可以连接到我们的应用中
export default new Vuex.Store({
  state,
  mutations
})
import Display from './Display.vue'
import Increment from './IncrementButton.vue'
import store from '../vuex/store' // import 我们刚刚创建的 store

export default {
  components: {
    Display: Display,
    Increment: Increment
  },
  store: store // 在根组件加入 store,让它的子组件和 store 连接
}

vuex/actions.js

第二步,创建action

components/Increment.vue

// action 会收到 store 作为它的第一个参数
// 既然我们只对事件的分发(dispatch 对象)感兴趣。(state 也可以作为可选项放入)
// 我们可以利用 ES6 的解构(destructuring)功能来简化对参数的导入
export const incrementCounter = function ({ dispatch, state }) {
  dispatch('INCREMENT', 1)
}
<template>
  <div>
    <button @click='increment'>Increment +1</button>
  </div>
</template>

<script>
import { incrementCounter } from '../vuex/actions'
export default {
  vuex: {
    actions: {
      increment: incrementCounter
    }
  }
}
</script>
  1. vuex.actions
  2. 无须指定store
  3. vuex.actions直接使用

修改vuex/actions.js

第三步,创建state和mutation

import Vue from 'vue'
import Vuex from 'vuex'

// 告诉 vue “使用” vuex
Vue.use(Vuex)

const state = {
  // 应用启动时,count 置为0
  count: 0
}

const mutations = {
  // mutation 的第一个参数是当前的 state
  // 你可以在函数里修改 state
  INCREMENT (state, amount) {
    state.count = state.count + amount
  }
}

// 整合初始状态和变更函数,我们就得到了我们所需的 store
// 至此,这个 store 就可以连接到我们的应用中
export default new Vuex.Store({
  state,
  mutations
})

components/Display.vue

第四步,在组件获取值

<template>
  <div>
    <h3>Count is {{ counterValue }}</h3>
  </div>
</template>

<script>
import { getCount } from '../vuex/getters'
export default {
  vuex: {
    getters: {
      counterValue: state => state.count
    }
  }
}
</script>

多模块场景

import Vue from 'vue'
import Vuex from '../../../src'
import * as actions from './actions'
// 导入各个模块的初始状态和 mutations
import cart from './modules/cart'
import products from './modules/products'

Vue.use(Vuex)

export default new Vuex.Store({
  // 组合各个模块
  modules: {
    cart,
    products
  }
})
store.cart.xxx

Vuex的细节

State:状态,单一状态树

 

Mutations:变更状态,本质是一个事件系统,必须是同步函数

 

Actions:操作,可以是同步函数,也可以是异步函数,用于分发 mutations 的函数

Vue.js 和 Vuex 的实践

vue-devtools

https://github.com/vuejs/vue-devtools

注意:需要使用dev版本的Vue.js

fis3,es6

1. components:Vue.js组件

2. lego_modules

3. modules

4. pages

 

项目目录结构

组件的使用

“子模块组件”

  1. 与当前业务有关
  2. 可在项目内复用
  3. 直接与 Vuex 打交道

“纯组件”

1. 与当前业务无关

2. 可以复用在其他项目中

 

例如 Vue.js版的BootStrap

<vs-alert 
  show.sync="showVariable"
  :state="state"
  dismissible>
  This is an alert
</vs-alert>
<div class="online">
    <video-play></video-play>

    <room-header></room-header>

    <room-button></room-button>
</div>

如果某个组件只用在特定的组件下,推荐将其放在其目录下

 

fis3+Vue.js

1. html inline到template中

2. css require

3. 引用其他组件import进来

mixins的妙用

引入Vuex

数据上报

其他

  • Vue.js 2.0,流式服务端渲染
  • Weex,“vue-native”
  • vue-router,构建SPA
  • webpack、异步组件

Copy of Vue.js 和 Vuex

By shengbowen

Copy of Vue.js 和 Vuex

  • 1,464