vue2 最佳实践

来源:http://slides.com/kalasoo/

掘金分享会摘取部分

一、

🌟4th Refactoring - Vue 2.0🌟

2016年12月 - 2017年2月

  1. 完全重新清理了文件结构(按照官方推荐最佳实践)
  2. 完全重新清理了 Web 业务架构
  3. 完全抽象了 Model 层的数据 CRUD 操作
  4. 完全重新写了 Webpack/Server 在不同环境下的配置
  5. 完全接入部分 SSR(根据需求)
  6. 完全接入新域名 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)
  }
}

  

 

 

 

 

posted @ 2018-05-22 15:59  五艺  阅读(222)  评论(0)    收藏  举报