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
- Vue.js 只支持IE9及以上,Q.js 支持IE6
- Vue.js 26K,Q.js 7K
- 文档、调试工具、功能、生态等的差距
Vue.js VS Angular
- 更轻量(简单、灵活)
- 指令和组件分得更清晰
- Angular双向绑定,Vue.js也支持,但默认为单向绑定
- 不使用脏检查,更好的性能
Vue.js VS Polymer
- Vue.js 的组件与 Polymer 中的自定义元素类似,但 Vue.js 兼容到IE9,Polymer 依赖最新的 Web 组件特性,不支持的浏览器中需polyfill
- 打包工具,Polymer 元素需要用专用工具 vulcanizer 打包
Vue.js VS React
- 真实DOM VS Virtual DOM
- .vue VS jsx
- 动画
- 学习成本
- 同构(Vue.js 2.0 才支持)
- 生态
Vue.js的一些探讨
- 简单例子
- 核心概念
- 实现原理
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
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>
- vuex.actions
- 无须指定store
- 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
项目目录结构
组件的使用
“子模块组件”
- 与当前业务有关
- 可在项目内复用
- 直接与 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、异步组件
Vue.js 和 Vuex
By helinjiang
Vue.js 和 Vuex
- 1,717