GithubHelp home page GithubHelp logo

blog's People

Contributors

chenbin92 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

飞冰月报 - 201908

image.png

重要进展

🚀 GUI 开发工具 iceworks

  • 发布 iceworks 3.0.0 版本 🎉 🎉 🎉
  • 新增 CLI 使用链路:无需下载,一步开启全新研发体验;
  • 升级产品功能:「项目管理」、「工程管理」和「物料市场」三大能力覆盖源码研发全流程;
  • 全新用户界面:提供多主题和多语言能力,满足开发者的个性化诉求


🐯 微前端解决方案 icestark

  • 发布 [email protected] 版本 
  • 基于路由和模块化管理多个独立应用
  • 支持不同应用独立仓库、独立开发与部署

 🐌 状态管理方案 icestore

  • 发布 [email protected] 版本
  • 核心功能包括包含中间件机制的支持,实现了框架的可扩展性,同时实现了对 IE 9+ 浏览器的兼容,扩展了框架的可适用场景
  • 发布官方调试中间件 [email protected],方便用户进行状态的追踪与调试

🐝 工程工具 ice-scripts

  • 优化自动 polyfill 能力,core-js 版本升级到 3.0,移除版本检测,优化启动速度
  • Mock 能力重构,支持 mock 内容热加载
  • 插件体系新增:
    • ice-plugin-jsx-plus 支持 jsx+ 语法
    • ice-plugin-dll 支持开启 DLLPlugin 提升构建速度

原创文章

  • icestore - 基于 Hooks 的轻量级状态管理方案:  在 React 的状态管理领域目前最有影响力的有两大流派,一类是以函数式编程为代表的 Redux,特点是通过单 store 与约定状态为不可变数据结构,使得状态易追踪与回溯,一类是以响应式编程为代表的 Mobx,特点是允许对状态进行直接修改,通过 observable 对 view 进行更新。自 [email protected] 发布 Hooks 特性之后,一大波基于 Hooks 的状态管理方案随之而来,飞冰团队也基于实际的业务场景产出了 icestore 方案,相比于 Redux、Mobx 在概念上会更少,也更贴近 React 的原生开发体验。

  • 飞冰团队的“寻人启事”:  飞冰隶属于阿里巴巴淘系前端团队的中后台架构小组,坐标杭州/北京。团队 16 年成立,3 年来我们持续在中后台领域耕耘,先后在内部孵化了可视化搭建平台(iceluna)、智能助手平台(iceman)、前端研发工作台(iceworks)等技术产品。在开源方面,我们主导的 ice 是阿里开源仓库中为数不多 star 数过万的前端项目

淘系前端精选 

  • 如何加快 Node.js 应用的启动速度:我们平时在开发部署 Node.js 应用的过程中,对于应用进程启动的耗时很少有人会关注,大多数的应用 5 分钟左右就可以启动完成,这个过程中会涉及到和集团很多系统的交互,这个耗时看起来也没有什么问题;但在 Serverless 的大潮下,如果我们的 Node.js FaaS 还像之前应用一样,一次部署耗时在分钟级,无法快速、有效地响应请求,甚至在脉冲请求时引发资源雪崩,那么一切的优势都将变成灾难,看看 Midway 团队的同学是如何解决的。

  • VSCode 原理解析 - 断点调试:VSCode 作为微软体系下一款当前最热的 IDE 开发工具,在调试领域上的探索实践是很好的学习案例,有道是:借他山之石,逐已身之玉,故本文着力于分析 VCode 中调试功能的设计与实现,让后来的人可以较为简单的理解调试这件事情是如何做到的。

  • 淘宝 Web 3D 应用与游戏开发实战:来自淘宝虚拟互动团队同学的分享,这个团队主攻 3D/游戏/VR/AR,为什么会在这样交叉领域去发力做一些事情?去年的双十一淘宝去年交易额多少?一千多亿,其中有 80% 的 GMV 是来自移动端的,简单地理解就是说我们公司在电商领域 80% 的钱是通过手机客户端赚取的,而不是 PC。这就是为什么在他们要在移动端做 3D/VR/AR 的应用。

业内精选

  • 微软提出 CSS Modules V1 :通过 import 语句将 CSS 模块导入到组件中:CSS Modules V1 是 Microsoft 提出的一项新建议,它是 ES Script 模块系统的一项扩展。在 CSS 模块的帮助下,Web 开发人员可以将 CSS 加载到组件定义中(例如 import styles from "styles.css"),并且与其他模块类型无缝对接。

  • React v16.9.0 and the Roadmap Update:不包含重大更改,旧的生命周期方法名在此版本继续沿用,但会给出警告提示,以及废弃 javascript: 形式的 URL,废弃 Factory 组件,新增用于测试的异步韩式 act() 等等。

  • Vue 最新动态:基于函数的组件 API 提议被拒绝,尤雨溪发布了重新设计的 Function-API RFC,现在被称为 Composition API。

其他

koa2-webpack-boilerplate docs

Table of Contents

  1. Features
  2. Requirements
  3. Installation
  4. Running the Project
  5. Project Structure
  6. Live Development
  7. Routing
  8. Webpack
  9. Eslint
  10. Pre-commit
  11. Base Configuration
  12. In development Mode
  13. In production Mode
  14. Mount
  15. TODO

Features

This is a starter koa boilerplate app I've put together using the following technologies:

koa v2

webpack v3

ES2015+

Babel

SCSS

Hot reload

Eslint

pre-commit


Requirements

  • node ^6.0.0
  • npm ^5.0.0

Installation

基于 koa-boilerpate 开始一个新的项目

$ git clone [email protected]:chenbin92/koa2-webpack-boilerplate.git MyApp
$ cd MyApp
$ npm install        # Install project dependencies listed in package.json

Running the Project

安装项目依赖成功后,启动开发环境命令如下

// run the dev server http://localhost:3000
$ npm run dev:start

其他任务脚本

npm <script> Description
star:dev Serves your app in development mode
star:prod Serves your app in production mode
build Builds the application
lint Lints the project for potential errors

Project Structure

一般项目结构可以按照文件类型功能类型或其他类型设计,每个团队每个项目都可能会有自己的项目结构。 koa2-webpack-boilerplate 奉行『约定优于配置』,按照一套统一的约定进行应用开发。

koa2-webpack-boilerpate
├── index.js                        # 用于自定义启动时的初始化工作(如配置babel-register)
├── src                             # 应用源代码
|   ├── assets                      # 静态资源
|   |   ├── images
|   |   ├── javascripts
|   |   └── stylesheets
|   ├── config                      # 用于编写配置文件
|   |   └── dictionary.js
|   | 
│   ├── controller                  # 用于解析用户的输入,处理后返回相应的结果
│   |   └── home.js
│   ├── service                     # 用于编写业务逻辑层
│   |   └── user.js
│   ├── middleware                  # 用于编写中间件
│   |   └── response_time.js
│   ├── public                      # 唯一对外开放的文件夹,存放静态文件和编译后的资源文件
│   |   └── favicon.ico
│   ├── view                        # 用于放置模板文件
│   |   └── home.html
│   └── router                     # 用于配置 URL 路由规则
│   |   └── index.js
├── build                           # 用于编写构建文件
|   ├── chalk.config.js
|   ├── project.config.js
|   └── webpack.prod.js
└── test                            # 用于单元测试

Live Development

HTML

我们使用 webpack 对 app/assets/* 目录下的文件进行动态编译打包至 app/public/* 目录下,通过 assetsMiddleware 中间件根据自动注入 bundle 文件。

大致思路是:

  • 在开发环境,直接使用webpack编译在内存的文件系统作为资源来源;
  • 在生产环境,先使用 assets-webpack-plugin 生成 assetsMap.json 文件,然后根据 assetName 映射;
  • ctx.state 上挂载 linkscript 属性,用于在 index.html 引用文件
ctx.state.script = (assetName) => {
     return `<script src='${getUrlByEnv(assetName)}'></script>`;
 };

 ctx.state.link = (assetName) => {
      return `<link rel='stylesheet' href='${getUrlByEnv(assetName)}'>`;
    };

完整代码

Images

应用图片默认的位置是 app/assets 文件夹中的 images,通过相关配置会监听并自动将图片自动映射到 app/public 目录下;

你可以这样引用图片:

// in HTML
<img src="images/egg_logo.svg" alt="logo">
// in SCSS
background-image: url("images/egg_logo.svg");

StyleSheets

推荐使用 SASS 进行样式编写;CSS 组织按照页面(page)和框架(framework)进行区分自定义的和第三方库的样式,通过 @import 导入到 application.css,目录结构形如:

├── stylesheets
|   ├── page
|   |    ├── homepage.csss
|   |    └── help.scss
|   ├── framework
|   |    ├── bootstrap.csss
|   |    └── button.scss
|   ├── application.scss

JavaScript

  • 应用按照功能模块化进行开发,主要有以下两种约定:

    • Global Module: 挂载在 window 对象
    • Namespace: 挂载在 app 对象上
  • 模块化的几种写法:

  • 方式一:挂载到 window 对象

    window.ModuleName = (function() {
      function Fn() {
        this.fn1();
      }
    
      Fn.prototype.fn1 = function() {}
    
      return Fn
    })();
    
  • 方式二:IIFE

    window.app = window.app || {};
    (function(app) {
      app.ModuleName = (function() {
        // your code...
      })();
    }).call(window, app);
    

    or :

    (function(global) {
      class ModuleName {
        // your code...
      }
    
      global.ModuleName = ModuleName
    })(window.appp || (window.app = {}));
    
  • 方式三:挂载到 app 对象上

    class ModuleName {
      // your code...
    }
    window.app = window.app || {};
    app.ModuleName = ModuleName;
    
  • 方式四: ES6 Class

    class ModuleName {
      constructor() {}
    
      fn() {}
    }
    export default ModuleName
    
  • 方式五: ...

  • 引用方式

    在应用的 app/assets/javascripts/application.js 文件包含下面几行代码:

    // import stylesheets
    import '../stylesheets/application.scss';
    
    // import page scripts
    import './home';
    import './about';
    

    在应用的 app/assets/javascripts/vendors.js 文件包含下面几行代码:

    // import Third-party libraries
    import $ from 'jquery';
    import _ from 'lodash';
    

    在 JavaScript 文件中,主要分为以下几个部分按照从上到下的顺序处理的:

    • 引入应用的样式
    • 引入第三方库
    • 引入业务模块

Routing

简单演示约定

import Router from 'koa-router';
import home from '../controller/home';
import about from '../controller/about';

const appRoutes = () => {
  // TODO: 添加前缀会导致静态资源无法加载
  const router = new Router({
    prefix: '/test',
  });

  router
    .get('/', home)
    .get('/about', about);

  return router;
};

export default appRoutes;



Eslint

项目遵循 eslint-egg 规则;在开发模式下进行 eslint watching,它可以有效的提示你对应的代码是否符合约定规则。

eslint watching


pre-commit

pre-commit 是一个 git 的勾子,它可以确保你在提交代码前需要通过你预设的相关约定;在脚手架中主要用来确保在你提交代码之前必须先通过所有的 Eslint 检查,否则不能提交。

pre-commit


Base configuration

  • Copy images
  • Sass compile
  • Generate html
  • Expose global
  • Define plugin
  • Assets webpack plugin

In Development mode

  • HOT
  • File watching
  • Eslint watching
  • Pre commit

In Production mode

  • Uglify javascript
  • Extract stylesheets
  • Extract the common file
  • Eslint watch
  • Bundle file analyzer
  • Static file md5

BundleAnalyzerPlugin


Mount

需求:如何在一个域名下根据项目名称作为前缀,同时挂载多个 web 应用。

例如:

根域名:http://www.upchina.com/

A 应用:http://www.upchina.com/A

B 应用:http://www.upchina.com/B

C 应用:http://www.upchina.com/C

解决方案:通过 Mount 解决,其**是把整个应用当作一个中间件,在 mount 内修改应用的 path,然后再次创建一个新的应用,将 mount 中间件传递

import Koa from 'koa'
import mount from 'mount'
import router from 'router'

// 传递给 mount 
const a = new Koa()
a.use(router().routes())

// app
const app = new Koa()
app.use(mount('/m', a))

app.listen(3001)

注意

  • 使用 Mount 只能用相对路径
  • 可以代理整个应用,也可以只代理某个路由

TODO

  • Hot Reload
  • ...

源代码

Dynamic imports with webpack

问题

使用 webpack 构建, 动态去加载 css 时,为什么使用 import() 会报错,使用 System.import() 可以?

$ webpack => 2.6.1

dynamic-load-css-file
dynamic-load-css-file-error


原因

require.ensure()

在 webpack1.x 中提供了 require.ensure() 方法动态导入文件,其函数签名如下:

require.ensure(dependencies, callback, chunkName)

简单示例:

require.ensure([], function(require) {
  const foo = require("./module");
});

注:require.ensure() 出自 CommonJS 中有一个 Modules/Async/A 规范,该规范定义了 require.ensure 语法。webpack 基于此基础,在打包的时候根据 require.ensure() 进行代码切分,并异步加载切分后的代码。

System.import()

ES6 Loader 规范定义了 System.import 方法,用于在运行时动态加载 ES6 模块,Webpack 把 System.import 作为拆分点;所以也可以通过 System.import() 方法实现动态引入,与 require.ensure()
不同的是 System.import() 返回的是一个 Promise,如:

System.import("./module")
  .then(module => console.log(module))
  .catch(err => console.log(Chunk loading failed))

import()

而在 webpack v2 (v2.1.0-beta.28) 废弃了 System.import() 方法,在 webpack v3 会完全删除System.import() 方法, 进而支持 ECMAScript State 3 草案阶段的原生 import() 方法来替代 System.import() 方法。

import("./module").then(module => {
    return module.default;
  }).catch(err => {
    console.log("Chunk loading failed");
  });

import

ES6 模块与 CommonJS 模块的差异 ,自行理解:

  • CommonJS

    • CommonJS 和 AMD 模块是运行时加载
    • CommonJS 模块就是对象,输入时必须查找对象属性
    • CommonJS 模块输出的是值的缓存,不存在动态更新
  • ES6 Module

    • ES6 模块是编译时加载
    • ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入
    • ES6 export 语句输出的接口,与其对应的值是动态绑定关系

由于 ES6 中的模块是编译时加载,import 命令会被 JavaScript 引擎静态分析,先于模块内的其他模块执行。所以,下面的代码会报错:

// 报错
if (x === 2) {
  import MyModual from './myModual';
}

注:在 Webpack 2(v2.2.0-rc.5) 中增加对 ES6 模块的原生支持。这意味着 Webpack 现在可以识别 import 和 export,不需要先把它们转换成 CommonJS 模块的格式。

require()、import、import()

require() 是 CommonJS 的实现,用于运行时加载,推荐阅读 require() 源码解读

import 是 ECMAScript 6 Module 的语法,import 是静态执行,推荐阅读 import 命令

import() 函数是 ECMAScript Stage 3 草案阶段的语法;用于完成动态加载即运行时加载,可以用在任何地方。import()函数 返回的是一个 Promise。类似于 CommonJs 的 require() ,区别主要是前者是异步加载,后者是同步加载,推荐阅读 proposal-dynamic-import

import() 的适用场景:

  • 按需加载
  • 条件加载
  • 动态的模块路径

结论

webpack 的 Dynamic Imports 实现主要是利用 ECMAScript的 import() 动态加载特性,而 import() 目前只是一个草案,如果需要用此方法,需要引入对应的转换器,如 babel-plugin-syntax-dynamic-import

模块化的实现对比

CommonJS AMD CMD ES6 Module
解决的问题 模块化开发 模块化开发 模块化开发 模块化开发
适用环境 服务端 浏览器 浏览器 浏览器/服务端
加载方式 同步加载/异步加载 异步加载 异步加载 同步加载/异步加载
模块定义 module.exports define(id?,dependencies?,factory) define(function(require,exports,module){ }) export
模块加载 require()/require.ensure() require([module], callback) require(module) import / import()
执行时机 提前执行 延迟执行
依赖原则 依赖前置 依赖就近
规范化的实现 require.js SeaJS
webpack 的支持 require.ensure() require() import / import()

参考

推荐阅读

写在最后

感谢 @Sean Larkin (webpack 核心开发者之一) 的指点,一眼看出了错误所在 ,那种感觉就像...你懂得...;值得记录一下 😀 😀 😀 😀 😀 😀

dynamic-imports-with-webpack-Chat-record

你可能不需要 Vuex

目录

  • 组件化
  • 组件通信
  • 状态管理
  • Vuex 是什么
  • Vuex 有什么特点
  • Vuex 解决了什么问题
  • 什么类型的数据适合放在 Vuex 管理
  • 工具
  • 总结
  • 参考
  • 扩展阅读

写这篇文章的主要目的是在现在的公司推荐使用 Vue,而在使用 Vue 的时候很多同事对为什么要使用 Vuex 不理解,我本身是没有使用过 Vue 或者 Vuex 写过实际项目;有过一年左右的 React 和 Redux 相关技术的项目实践,主要是根据对等的一点经验所写;不当之处,欢迎讨论。

组件化

Web Components提供了一种组件化的推荐方式,具体来说,就是:

  • 通过shadow DOM封装组件的内部结构
  • 通过Custom Element对外提供组件的标签
  • 通过Template Element定义组件的HTML模板
  • 通过HTML imports控制组件的依赖加载

所谓组件化,核心意义莫过于提取真正有复用价值的东西。那怎样的东西有复用价值呢?

  • 控件
  • 基础逻辑功能
  • 公共样式
  • 稳定的业务逻辑

-- 摘自xufei的《2015前端组件化框架之路》

对组件的粒度进行细分,可以分为:

  • UI component: 纯 UI 组件,可以维护本地的 UI State,接收 props 作为数据渲染,保持纯函数形式,具有可复用性。
  • Logic component: 带有逻辑的 UI 组件,与数据打交道。

组件化这个词,在 UI 这一层通常指“标签化”,也就是把大块的业务界面,拆分成若干小块,然后进行组装。狭义的组件化一般是指标签化,也就是以自定义标签(自定义属性)为核心的机制。广义的组件化包括对数据逻辑层业务梳理,形成不同层级的能力封装。

components


组件通信

应用在组件化之后,组件之间必然存在某种联系;组件化意味着协同工作,通常存在着 父子组件兄弟组件跨级组件 等组件关系,那么组件之间如何进行协调工作,即组件通信;

在 Vue 中,父子组件的关系可以总结为 props downevents up

  • 父子组件通信:父组件通过 props 向下传递数据给子组件
  • 子父组件通信:子组件通过 events 给父组件发送消息
    • 使用 $on(eventName) 监听事件
    • 使用 $emit(eventName) 触发事件
  • 非父子组件通信:使用一个空的 Vue 实例作为**事件总线

component communication

可以想象到在简单的 父子子父 组件之间的通信是很轻松的,通过 propsevents 即可实现;但是往往我们的应用可能不只有这么简单的层级关系,在多层跨级组件如果通过 props 去传递,那意味着一层一层的往子组件传递,最终你可能不知道当前组件的数据最终来自哪个父组件(当然你可以逆着方向一层一层往上找),通过 events 事件机制显然也存在着类似的问题。如果你觉得这样也可以接受,你可能不需要 Vuex;但如果你在想有没有什么好的模式优雅的去解决,你可以继续阅读下面的部分。


状态管理

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。

管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。
-- 摘自《Redux 中文文档》

在应用中,组件之间的通信其实是归根于应用的状态管理;而应用的状态是来自多方面的,如何对状态进行管理,提高代码的可维护性,提升开发效率;大多数主流框架对数据状态管理也都有了对应的方案:

  • React 专注于 UI 层,社区为其提供了 Redux、Mbox 等状态管理库
  • Vue 的团也提供了 Vuex 状态管理库
  • 还有一些专门解决数据层的库,如 RxJS

回到本文的讨论点,这里我们暂且只讨论 Vue;Vue 的核心库只关注视图层,单文件组件,其模板、逻辑和样式是内部耦合的,侧重数据和视图的同步;Vue 本身并没有对数据状态的管理进行处理,但其提供了另外一个类似 Redux 的解决方案 Vuex,一个集中式状态管理的库;也就是说,你可能不需要 Vuex,它只是对你应用状态进行管理的一个库。


Vuex 是什么

Vuex 是一个专门为 Vue.js 应用所设计的集中式状态管理架构
-- from vuex docs (updated in 2016-05-27)

...

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
-- from vuex docs (updated in 207-05-20)

上面的定义是摘自 Vuex 在 GitHub 上的文档,分别于 2016/05/27 和 2017/05/20 的更新记录;从中我们可以梳理一下关键词:

  • 集中式状态管理模式(注意是强调管理应用的所有组件的状态)
  • 可预测(前提是以相应的规则作为保证)

先不着急看下面的内容,如果你看到以上两点特性,脑海已经有了答案,能很好的向你的同事解释清楚,前提是你的同事完全没有任何 Vuex 的经验,那下面的内容你可以直接忽略。你也应该知道你的应用是否需要 Vuex 了

Vuex 有什么特点

从上面的定义中可以知道 Vuex 的特点其实就是下面两点:

  • 集中式状态管理
  • 可预测

什么是集中式状态管理模式

在说集中式管理模式之前,我们可以先来想想常见的处理方式是怎样的,即每个组件维护自身的数据和状态,自给自足,分而治之;其思路大致如下:

  • 定义组件自身的初始数据
  • 在组件内获取异步数据
  • 根据数据渲染更新视图
// 渲染视图
<template>
  <h2>single file component</h2>
<template>

<script>
  export default {
    // 初始数据
    data() {
    
    },
    
    // 获取异步数据
    created() {
      this.fetchData()
    },
    
    methods {
      fetchData() {
        // do something
      }
    }
  }
</script>

可简单对比 React 的思路:

// https://github.com/xufei/blog/issues/19#issuecomment-85989838

var render = function(Model) {
  return View;
}
event_loop(function(){
  var currentView = render(currentModel);
  map_view_into_user_interface(currentView);
})

分治带来的是可管理性,把组件设想成一个单一的东西,一个组件包含了自身需要的数据和视图,把查询逻辑封装在内部,外部只需要实现一个响应事件获取事件的东西基于可以了。即 Single File Component 概念,组件化后,整个应用的树结构可以一目了然,可以随意添加或者移除一个组件,而不会影响其他的组件,听起来很美好;但事情并非那么完美,由于这种方式封装的组件的内部实现聚合了异步请求的数据和自身的状态,真正组装复用起来是存在一定问题的。比如:

  • 在同一可视区域的冗余请求数
  • 不同层级组件的数据共享问题
  • ...

vue data flow

集中式状态管理模式则以一个全局单例模式管理应用的状态,类似于全局对象,但不完全一样。

  • Vuex 的状态管理存储是响应式的:就是当你的组件使用到了 Vuex 的某个状态,一旦它发生改变了,所有关联的组件都会自动更新相对应的数据。
    store

  • 不能直接修改 Vuex 的状态:修改 Vuex 的状态唯一途径是提交(commit) mutations 来实现修改

vuex flow

如上图,Vuex为Vue Components建立起了一个完整的生态圈,包括开发中的API调用一环。围绕这个生态圈,简要介绍一下各模块在核心流程中的主要功能:

  • Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
  • dispatch:操作行为触发方法,是唯一能执行action的方法。
  • actions:操作行为处理模块。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
  • commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
  • mutations:状态改变操作方法。是Vuex修改state的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
  • state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
  • getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

Vue组件接收交互行为,调用dispatch方法触发action相关处理,若页面状态需要改变,则调用commit方法提交mutation修改state,通过getters获取到state新值,重新渲染Vue Components,界面随之更新

建议类比 Redux 的流程,大体上是相同的:

集中式状态管理的好处

相对于分治(碎片化)的状态管理,多个状态分散的跨越在不同组件交互在各个角落,每个 View 会有相对应的 Model 维护状态;而集中式管理模式则用于将分散于组件的状进行集中化管理,提供一个全局的 store 存储管理应用的状态。集中式的状态管理可以让整体的状态变化更加明晰,尤其是配合各自的 devtools。它具备以下特点:

  • components share state(组件之间共享状态)
  • state should be accessible from everywhere(所有状态可以方便获取)
  • components need to mutate the state(组件可以修改状态)
  • components need to mutate the state of another component(组件可以修改其他组件的状态)

vuex-store-gif

集中式状态管理的弊端

上面提到集中式存储管理应用的所有组件的状态,而应用的状态上文已经有提到,这里大致可以分为:

  • UI 状态:用户输入的状态
  • 数据状态:服务端传过来的数据状态
  • 客户端信息:设备信息的状态
  • 其他...

这里是有歧义的,集中式存储管理应用的所有状态,按照字面意思是将所有的状态都集中式管理,也就是存到 Vuex 的全局单一 store 中,显然我们是不能这样去理解的,应该视应用场景而定的,大致也可以分为以下几种:

  • 对于用户输入的状态,比如控制模态框的显示隐藏,我们一般在组件内处理消化;对于需要需要跨组件通信的,则可以存储在全局的 store 中,我们可以将这一类状态称之为本地状态(local state)。

  • 对于服务端传过来的数据状态,按照大多数的实践是存储在全局的 store 中,这样可以在任意的组件中都可以使用;当然,也可以只将多组件的共享的数据存储在全局的 store 中,单个组件需要的数据内部处理消化,组件销毁时对应的数据状态也会销毁。

对于客户端的信息或者一些其他的数据状态与上面两种方式在一定程度上也是相似的。这一切看起来并没什么问题,然而细细想想,当一个应用的足够复杂时,我们该如何去设计我们的数据模型,本地共享的状态是存在 store 还是通过事件机制去处理,服务端的数据是一股脑都塞给全局的 store 存在内存里还是视应用场景而定,在 Vuex 的文档或者是 Redux 文档这都没有唯一的答案。假设服务端传过来的数据都存在 store, 那最终的 store 会有多大,这是一个值得探索的问题。那究竟什么样的数据适合存储在全局的 store 中?

另外,使用 Vuex 必须按照上述 Vuex 的工作流程去进行,定义对应的 actionsmutations等等,这显然是在强制约定你以相应的规则去编写你的应用,对大多数新人来说,这是繁琐的。就相当于你得到了一定的好处,那你也得相应的有所付出。

什么类型的数据适合放在 Vuex 管理

至此,我们大概讨论了由于组件化,会产生组件间相互通信数据管理的问题,对此也有相应了的解决办法;然而,并没有一种很好的方式告诉我们到底什么类型的数据适合放在单一的 store 进行管理;回到 Vuex 的定义,将数据使用 Vuex 管理的主要原因之一是解决组件间的数据共享

所谓共享指的是,同一份数据被多处组件使用,并且要保持一定程度的同步:

store-flow

故而,在开发应用时,如何设计抽象数据层,这个是没有唯一答案的。但如果明白了其间的利弊,比如独立了数据层,视图的职责就非常单一,无非就是根据订阅的数据渲染页面,视图组件间的通信就会很少,大家都会去跟数据层交互,维护一份统一的数据结构。


工具

到这里,你应该可以确定你的应用是否应该使用 Vuex 了,如果你使用了 Vuex,那么它还有一些其他的附属产品;如下面的 vue-tool 调试工具,它可以让你对你的应用状态了如指掌,保存状态快照,历史回滚/时光旅行等等特性。

vue-devtool-demo

总结

合久必分,分久必合;Vue 提倡 Single File Component 概念将单一功能进行组件化封装,而 Vuex 的设计则是将分散在各处的状态进行合并集中管理的抽象模式。利弊在上面的文章也已说明,它是一种可选的方案,你用或者不用,取决于你的应用。

参考

注:对组件化和状态管理方面主要参考徐飞的系列文章,严重推荐深度阅读徐飞关于 《web 应用》《随笔系列》 文章

扩展阅读

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.