vue2 最佳实践
来源:http://slides.com/kalasoo/
掘金分享会摘取部分
一、
🌟4th Refactoring - Vue 2.0🌟
2016年12月 - 2017年2月
- 完全重新清理了文件结构(按照官方推荐最佳实践)
- 完全重新清理了 Web 业务架构
- 完全抽象了 Model 层的数据 CRUD 操作
- 完全重新写了 Webpack/Server 在不同环境下的配置
- 完全接入部分 SSR(根据需求)
- 完全接入新域名 juejin.im
整个 Web 项目
juejin/ ├── backend/ # 后端业务 ├── build/ # build 脚本 ├── cache/ # 页面 Redis 缓存 ├── config/ # prod/dev build config 文件 ├── src/ # Vue.js 核心业务 ├── static/ # 静态文件 ├── cdn.js # CDN 上传脚本 ├── index.html # 最基础的网页 ├── ... ├── renderer.js # SSR 脚本 └── server.js # Express 后端处理请求
{
...
"scripts": {
"start": "cross-env NODE_ENV=production node server",
"test": "cross-env NODE_ENV=production WRK_CNT=1 node server",
"stop": "pkill --signal SIGINT juejin-web",
"dev": "supervisor -q -n error -w build,config,backend server",
"build": "rimraf dist && npm run build:client && npm run build:server && npm run build:version",
"build:client": "cross-env NODE_ENV=production VUE_ENV=client webpack
--config build/webpack.client.conf.js --progress --hide-modules",
"build:server": "cross-env NODE_ENV=production VUE_ENV=server webpack
--config build/webpack.server.conf.js --progress --hide-modules",
"build:version": "cross-env NODE_ENV=production webpack
--config build/webpack.version.conf.js --progress --hide-modules",
"cdn": "node cdn",
"lint": "eslint --ext .js,.vue src"
},
...
}
Vue 核心文件
src/ ├── api/ # 接入微服务的基础 API ├── App/ # App Root Component ├── asset/ # 静态文件 ├── business/ # 业务 ├── component/ # 组件 ├── const/ # 常量 ├── event-bus/ # Event Bus 事件总线,类似 EventEmitter ├── global/ # 通用定义的 directive, mixin 还有绑定到 Vue.prototype 的函数 ├── model/ # Model 抽象层 ├── repository/ # 仓库,接入 Vuex 中 ├── router/ # 路由 ├── service/ # 服务 ├── state/ # Vuex 状态管理 ├── style/ # 样式 ├── util/ # 通用 utility functions ├── view/ # 各个页面 ├── client-entry.js # 前端业务 & build ├── server-entry.js # SSR业务 & build ├── ... └── main.js # Vue Object Initiation
基础设施层
api/
util/
这个比较容易理解
领域层 Domain
service/
各个 Domain 下的基础功能业务
repository/
某一个独立 Domain 下的获取数据的业务
model/
数据抽象层
业务层
business/
各个 Domain 下的具体业务,会引用 service 和 repository 中定义的功能
validator/
不同数据的 validation 过程
表现层
state/ router/ component/ view/
Vue 下具体的交互展示层业务
Event Bus
类似于 Node 中的 EventEmitter
通过事件管理和监听处理异常、Alert、Scroll 触发等
SSR
需求:
后端渲染解决性能问题
解决方案:
多层缓存
- 数据层缓存
- 组件层缓存 lru-cache
- 页面层缓存 redis
function createRenderer (bundle) {
return vueServerRenderer.createBundleRenderer(bundle, {
cache: LRU({
max: 5000,
maxAge: 1000 * 60 * 60
}),
directives: {
link: require('./src/global/directive/link').server
}
})
}
renderStream.on('end', () => {
if (!shouldContinue) { return }
const initialState = encodeURIComponent(context.initialState)
const html = `
<script id="jjis">
window.__JJIS__="${initialState}"
</script>
${htmlTemplate[2]}
`
cachedHtml = cachedHtml + html
if (!user && context.code === 200) {
cache({
url: req.originalUrl,
html: cachedHtml
})
}
res.end(html)
})
数据一致性
需求:
前后端渲染数据一致性问题
解决方案:
-
通过某种事件广播机制实现数据的最终一致性
-
Vuex 本身就有事件广播模型,我们定义了 3 个 mutation 类型:
-
ENTITY_CREATED - 实体已创建
-
ENTITY_UPDATED - 实体已更新
-
ENTITY_DELETED - 实体已删除
-
import {
ENTITY_CREATED,
ENTITY_UPDATED,
ENTITY_DELETED
} from 'state/type'
let store = null
export function setup (tarStore) {
store = tarStore
}
export function emitEntityCreatedEvent (model, data) {
return store.commit(ENTITY_CREATED, { model, data })
}
export function emitEntityUpdatedEvent (model, data) {
return store.commit(ENTITY_UPDATED, { model, data })
}
export function emitEntityDeletedEvent (model, data) {
return store.commit(ENTITY_DELETED, { model, data })
}
import {
LIKE_ENTRY,
...
} from 'state/type'
import {
emitEntityUpdatedEvent,
emitEntityDeletedEvent
} from 'state/entity-event'
import * as entryService from 'service/entry'
import * as entryRepository from 'repository/entry'
const UPDATE_STATE = 'entry/UPDATE_STATE'
export default {
state: {},
mutations: {
[UPDATE_STATE] (state, newState) {
Object.assign(state, newState)
}
},
actions: {
[LIKE_ENTRY] ({ commit }, entry) {
return entryService.likeEntry(entry.id)
.then(() => {
entry.liked = true
entry.collectionCount += 1
})
},
...,
[ADD_ENTRY] ({ commit }, entry) {
return entryRepository.add(entry)
},
[UPDATE_ENTRY] ({ commit }, entry) {
return entryRepository.update(entry)
.then(() => emitEntityUpdatedEvent('Entry', entry))
},
[DELETE_ENTRY] ({ commit }, entryId) {
return entryRepository.remove(entryId)
.then(() => emitEntityDeletedEvent('Entry', { id: entryId }))
}
}
}
完全通过事件传递行为
this.$store.dispatch(DELETE_ENTRY, entryId)
// src/state/module/common/entry.js
[DELETE_ENTRY] ({ commit }, entryId) {
return entryRepository.remove(entryId)
.then(() => emitEntityDeletedEvent('Entry', { id: entryId }))
}
[ENTITY_DELETED] (state, info) {
if (shouldReact(info)) {
state.list = state.list.filter(item => item.id !== info.data.id)
}
}

浙公网安备 33010602011771号