V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  answershuto  ›  全部回复第 1 页 / 共 1 页
回复总数  16
2017-10-30 11:17:59 +08:00
回复了 answershuto 创建的主题 前端开发 Vuex 源码解析
@smallpath 不错~其实主要就是抽离核心的 Store 操作,以及最关键用 Vue 对象来实现数据的“响应式化”。
2017-10-30 11:02:53 +08:00
回复了 answershuto 创建的主题 前端开发 Vuex 源码解析
@SourceMan 👌,我下次注意~
2017-10-30 08:17:26 +08:00
回复了 answershuto 创建的主题 前端开发 Vuex 源码解析
### unregisterModule

```javascript
/* 注销一个动态 module */
unregisterModule (path) {
/* 转化称 Array */
if (typeof path === 'string') path = [path]

if (process.env.NODE_ENV !== 'production') {
assert(Array.isArray(path), `module path must be a string or an Array.`)
}

/*注销*/
this._modules.unregister(path)
this._withCommit(() => {
/* 获取父级的 state */
const parentState = getNestedState(this.state, path.slice(0, -1))
/* 从父级中删除 */
Vue.delete(parentState, path[path.length - 1])
})
/* 重制 store */
resetStore(this)
}
```

同样,与 registerModule 对应的方法 unregisterModule,动态注销模块。实现方法是先从 state 中删除模块,然后用 resetStore 来重制 store。

### resetStore

```javascript
/* 重制 store */
function resetStore (store, hot) {
store._actions = Object.create(null)
store._mutations = Object.create(null)
store._wrappedGetters = Object.create(null)
store._modulesNamespaceMap = Object.create(null)
const state = store.state
// init all modules
installModule(store, state, [], store._modules.root, true)
// reset vm
resetStoreVM(store, state, hot)
}
```

这里的 resetStore 其实也就是将 store 中的_actions 等进行初始化以后,重新执行 installModule 与 resetStoreVM 来初始化 module 以及用 Vue 特性使其“响应式化”,这跟构造函数中的是一致的。


## 插件

Vue 提供了一个非常好用的插件[Vue.js devtools]( https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)

```javascript
/* 从 window 对象的__VUE_DEVTOOLS_GLOBAL_HOOK__中获取 devtool 插件 */
const devtoolHook =
typeof window !== 'undefined' &&
window.__VUE_DEVTOOLS_GLOBAL_HOOK__

export default function devtoolPlugin (store) {
if (!devtoolHook) return

/* devtoll 插件实例存储在 store 的_devtoolHook 上 */
store._devtoolHook = devtoolHook

/* 出发 vuex 的初始化事件,并将 store 的引用地址传给 deltool 插件,使插件获取 store 的实例 */
devtoolHook.emit('vuex:init', store)

/* 监听 travel-to-state 事件 */
devtoolHook.on('vuex:travel-to-state', targetState => {
/* 重制 state */
store.replaceState(targetState)
})

/* 订阅 store 的变化 */
store.subscribe((mutation, state) => {
devtoolHook.emit('vuex:mutation', mutation, state)
})
}
```

如果已经安装了该插件,则会在 windows 对象上暴露一个__VUE_DEVTOOLS_GLOBAL_HOOK__。devtoolHook 用在初始化的时候会触发“ vuex:init ”事件通知插件,然后通过 on 方法监听“ vuex:travel-to-state ”事件来重置 state。最后通过 Store 的 subscribe 方法来添加一个订阅者,在触发 commit 方法修改 mutation 数据以后,该订阅者会被通知,从而触发“ vuex:mutation ”事件。

## 最后

Vuex 是一个非常优秀的库,代码量不多且结构清晰,非常适合研究学习其内部实现。最近的一系列源码阅读也使我自己受益匪浅,写这篇文章也希望可以帮助到更多想要学习探索 Vuex 内部实现原理的同学。
2017-09-18 20:57:12 +08:00
回复了 answershuto 创建的主题 前端开发 VirtualDOM 与 diff(Vue 实现)
由于平台字数限制放不下了,接上文。

## DOM 操作

由于 Vue 使用了虚拟 DOM,所以虚拟 DOM 可以在任何支持 JavaScript 语言的平台上操作,譬如说目前 Vue 支持的浏览器平台或是 weex,在虚拟 DOM 的实现上是一致的。那么最后虚拟 DOM 如何映射到真实的 DOM 节点上呢?

Vue 为平台做了一层适配层,浏览器平台见[/platforms/web/runtime/node-ops.js]( https://github.com/answershuto/learnVue/blob/master/vue-src/platforms/web/runtime/node-ops.js)以及 weex 平台见[/platforms/weex/runtime/node-ops.js]( https://github.com/answershuto/learnVue/blob/master/vue-src/platforms/weex/runtime/node-ops.js)。不同平台之间通过适配层对外提供相同的接口,虚拟 DOM 进行操作真实 DOM 节点的时候,只需要调用这些适配层的接口即可,而内部实现则不需要关心,它会根据平台的改变而改变。

现在又出现了一个问题,我们只是将虚拟 DOM 映射成了真实的 DOM。那如何给这些 DOM 加入 attr、class、style 等 DOM 属性呢?

这要依赖于虚拟 DOM 的生命钩子。虚拟 DOM 提供了如下的钩子函数,分别在不同的时期会进行调用。

```JavaScript
const hooks = ['create', 'activate', 'update', 'remove', 'destroy']

/*构建 cbs 回调函数,web 平台上见 /platforms/web/runtime/modules*/
for (i = 0; i < hooks.length; ++i) {
cbs[hooks[i]] = []
for (j = 0; j < modules.length; ++j) {
if (isDef(modules[j][hooks[i]])) {
cbs[hooks[i]].push(modules[j][hooks[i]])
}
}
}
```

同理,也会根据不同平台有自己不同的实现,我们这里以 Web 平台为例。Web 平台的钩子函数见[/platforms/web/runtime/modules]( https://github.com/answershuto/learnVue/tree/master/vue-src/platforms/web/runtime/modules)。里面有对 attr、class、props、events、style 以及 transition (过渡状态)的 DOM 属性进行操作。

以 attr 为例,代码很简单。

```JavaScript
/* @flow */

import { isIE9 } from 'core/util/env'

import {
extend,
isDef,
isUndef
} from 'shared/util'

import {
isXlink,
xlinkNS,
getXlinkProp,
isBooleanAttr,
isEnumeratedAttr,
isFalsyAttrValue
} from 'web/util/index'

/*更新 attr*/
function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) {
/*如果旧的以及新的 VNode 节点均没有 attr 属性,则直接返回*/
if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {
return
}
let key, cur, old
/*VNode 节点对应的 Dom 实例*/
const elm = vnode.elm
/*旧 VNode 节点的 attr*/
const oldAttrs = oldVnode.data.attrs || {}
/*新 VNode 节点的 attr*/
let attrs: any = vnode.data.attrs || {}
// clone observed objects, as the user probably wants to mutate it
/*如果新的 VNode 的 attr 已经有__ob__(代表已经被 Observe 处理过了), 进行深拷贝*/
if (isDef(attrs.__ob__)) {
attrs = vnode.data.attrs = extend({}, attrs)
}

/*遍历 attr,不一致则替换*/
for (key in attrs) {
cur = attrs[key]
old = oldAttrs[key]
if (old !== cur) {
setAttr(elm, key, cur)
}
}
// #4391: in IE9, setting type can reset value for input[type=radio]
/* istanbul ignore if */
if (isIE9 && attrs.value !== oldAttrs.value) {
setAttr(elm, 'value', attrs.value)
}
for (key in oldAttrs) {
if (isUndef(attrs[key])) {
if (isXlink(key)) {
elm.removeAttributeNS(xlinkNS, getXlinkProp(key))
} else if (!isEnumeratedAttr(key)) {
elm.removeAttribute(key)
}
}
}
}

/*设置 attr*/
function setAttr (el: Element, key: string, value: any) {
if (isBooleanAttr(key)) {
// set attribute for blank value
// e.g. <option disabled>Select one</option>
if (isFalsyAttrValue(value)) {
el.removeAttribute(key)
} else {
el.setAttribute(key, key)
}
} else if (isEnumeratedAttr(key)) {
el.setAttribute(key, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true')
} else if (isXlink(key)) {
if (isFalsyAttrValue(value)) {
el.removeAttributeNS(xlinkNS, getXlinkProp(key))
} else {
el.setAttributeNS(xlinkNS, key, value)
}
} else {
if (isFalsyAttrValue(value)) {
el.removeAttribute(key)
} else {
el.setAttribute(key, value)
}
}
}

export default {
create: updateAttrs,
update: updateAttrs
}

```

attr 只需要在 create 以及 update 钩子被调用时更新 DOM 的 attr 属性即可。
2017-09-11 10:25:49 +08:00
回复了 answershuto 创建的主题 JavaScript 说说 VNode 节点(Vue.js 实现)
@dcoder 😊3q
2017-09-11 10:25:30 +08:00
回复了 answershuto 创建的主题 JavaScript 说说 VNode 节点(Vue.js 实现)
@sansansan333 时间花下去了,总会有收获的哈~
2017-09-11 10:25:03 +08:00
回复了 answershuto 创建的主题 JavaScript 说说 VNode 节点(Vue.js 实现)
@codermagefox 哈哈谢谢
2017-08-30 18:11:45 +08:00
回复了 answershuto 创建的主题 分享创造 一款可视化编辑手机 H5 页面的单页应用 WebApp
@biuuu 😂 不带解释以为是骂人的。。。
2017-08-30 12:23:37 +08:00
回复了 answershuto 创建的主题 分享创造 一款可视化编辑手机 H5 页面的单页应用 WebApp
@lqzhgood 作为技术可能多多少少对这些名称不是很喜欢,但是我似乎找不到别的更合适且让人熟悉的名词去描述 H5 这个东西了。
2017-08-30 09:03:31 +08:00
回复了 answershuto 创建的主题 分享创造 一款可视化编辑手机 H5 页面的单页应用 WebApp
@v1024 H5 按照我的理解是现在常用于微信里做营销用的类似 ppt 的 HTML5 页面的称呼,所以我没用 HTML5 而用了 H5,因为这个项目就是为了产出 H5 的页面的。
2017-08-21 17:26:14 +08:00
回复了 answershuto 创建的主题 Vue.js Vue.js 响应式原理
原文入口写错了,这里贴一下 https://github.com/answershuto/learnVue
2017-04-05 09:28:56 +08:00
回复了 answershuto 创建的主题 分享创造 爬虫租房
@saxon 木有 python 版本,原理都一样的,可以尝试做一个呀。。
2017-04-05 09:28:32 +08:00
回复了 answershuto 创建的主题 分享创造 爬虫租房
@yuedingwangji 哈哈,其实 url 修改一下就完全可以变成广州租房了。
2017-04-03 10:47:05 +08:00
回复了 answershuto 创建的主题 分享创造 爬虫租房
@awolfly9 挺不错的,我下次用用~
2017-03-17 17:31:05 +08:00
回复了 stdying 创建的主题 V2EX 如何添加图片
2017-03-17 17:27:51 +08:00
回复了 stdying 创建的主题 V2EX 如何添加图片
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1093 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 158ms · UTC 18:49 · PVG 02:49 · LAX 10:49 · JFK 13:49
Developed with CodeLauncher
♥ Do have faith in what you're doing.