chenbin92 / blog Goto Github PK
View Code? Open in Web Editor NEW个人博客
个人博客
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。
This is a starter koa boilerplate app I've put together using the following technologies:
✓ koa v2
✓ ES2015+
✓ Babel
✓ SCSS
✓ Eslint
^6.0.0
^5.0.0
基于 koa-boilerpate
开始一个新的项目
$ git clone [email protected]:chenbin92/koa2-webpack-boilerplate.git MyApp
$ cd MyApp
$ npm install # Install project dependencies listed in package.json
安装项目依赖成功后,启动开发环境命令如下
// 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 |
一般项目结构可以按照文件类型
、功能类型
或其他类型设计,每个团队每个项目都可能会有自己的项目结构。 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 # 用于单元测试
我们使用 webpack 对 app/assets/*
目录下的文件进行动态编译打包至 app/public/*
目录下,通过 assetsMiddleware 中间件根据自动注入 bundle 文件。
大致思路是:
assets-webpack-plugin
生成 assetsMap.json
文件,然后根据 assetName
映射;ctx.state
上挂载 link
和 script
属性,用于在 index.html
引用文件ctx.state.script = (assetName) => {
return `<script src='${getUrlByEnv(assetName)}'></script>`;
};
ctx.state.link = (assetName) => {
return `<link rel='stylesheet' href='${getUrlByEnv(assetName)}'>`;
};
应用图片默认的位置是 app/assets 文件夹中的 images,通过相关配置会监听并自动将图片自动映射到 app/public 目录下;
你可以这样引用图片:
// in HTML
<img src="images/egg_logo.svg" alt="logo">
// in SCSS
background-image: url("images/egg_logo.svg");
推荐使用 SASS 进行样式编写;CSS 组织按照页面(page)和框架(framework)进行区分自定义的和第三方库的样式,通过 @import
导入到 application.css
,目录结构形如:
├── stylesheets
| ├── page
| | ├── homepage.csss
| | └── help.scss
| ├── framework
| | ├── bootstrap.csss
| | └── button.scss
| ├── application.scss
应用按照功能模块化进行开发,主要有以下两种约定:
模块化的几种写法:
方式一:挂载到 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 文件中,主要分为以下几个部分按照从上到下的顺序处理的:
简单演示约定
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-egg
规则;在开发模式下进行 eslint watching,它可以有效的提示你对应的代码是否符合约定规则。
pre-commit
是一个 git
的勾子,它可以确保你在提交代码前需要通过你预设的相关约定;在脚手架中主要用来确保在你提交代码之前必须先通过所有的 Eslint
检查,否则不能提交。
需求:如何在一个域名下根据项目名称作为前缀,同时挂载多个 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)
注意
请问作者的画图和录制gif,分别是用的什么软件呢?谢谢
使用 webpack 构建, 动态去加载 css 时,为什么使用 import()
会报错,使用 System.import()
可以?
$ webpack => 2.6.1
在 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()
进行代码切分,并异步加载切分后的代码。
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))
而在 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");
});
ES6 模块与 CommonJS 模块的差异 ,自行理解:
CommonJS
ES6 Module
由于 ES6 中的模块是编译时加载,import
命令会被 JavaScript 引擎静态分析,先于模块内的其他模块执行。所以,下面的代码会报错:
// 报错
if (x === 2) {
import MyModual from './myModual';
}
注:在 Webpack 2(v2.2.0-rc.5) 中增加对 ES6 模块的原生支持。这意味着 Webpack 现在可以识别 import 和 export,不需要先把它们转换成 CommonJS 模块的格式。
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 核心开发者之一) 的指点,一眼看出了错误所在 ,那种感觉就像...你懂得...;值得记录一下 😀 😀 😀 😀 😀 😀
写这篇文章的主要目的是在现在的公司推荐使用 Vue,而在使用 Vue 的时候很多同事对为什么要使用 Vuex 不理解,我本身是没有使用过 Vue 或者 Vuex 写过实际项目;有过一年左右的 React 和 Redux 相关技术的项目实践,主要是根据对等的一点经验所写;不当之处,欢迎讨论。
Web Components提供了一种组件化的推荐方式,具体来说,就是:
所谓组件化,核心意义莫过于提取真正有复用价值的东西。那怎样的东西有复用价值呢?
-- 摘自xufei的《2015前端组件化框架之路》
对组件的粒度进行细分,可以分为:
组件化这个词,在 UI 这一层通常指“标签化”,也就是把大块的业务界面,拆分成若干小块,然后进行组装。狭义的组件化一般是指标签化,也就是以自定义标签(自定义属性)为核心的机制。广义的组件化包括对数据逻辑层业务梳理,形成不同层级的能力封装。
应用在组件化之后,组件之间必然存在某种联系;组件化意味着协同工作,通常存在着 父子组件
、兄弟组件
、跨级组件
等组件关系,那么组件之间如何进行协调工作,即组件通信;
在 Vue 中,父子组件的关系可以总结为 props down
、events up
。
props
向下传递数据给子组件events
给父组件发送消息
$on(eventName)
监听事件$emit(eventName)
触发事件可以想象到在简单的 父子
,子父
组件之间的通信是很轻松的,通过 props
和 events
即可实现;但是往往我们的应用可能不只有这么简单的层级关系,在多层跨级组件如果通过 props
去传递,那意味着一层一层的往子组件传递,最终你可能不知道当前组件的数据最终来自哪个父组件(当然你可以逆着方向一层一层往上找),通过 events
事件机制显然也存在着类似的问题。如果你觉得这样也可以接受,你可能不需要 Vuex;但如果你在想有没有什么好的模式优雅的去解决,你可以继续阅读下面的部分。
随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。
管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。
-- 摘自《Redux 中文文档》
在应用中,组件之间的通信其实是归根于应用的状态管理;而应用的状态是来自多方面的,如何对状态进行管理,提高代码的可维护性,提升开发效率;大多数主流框架对数据状态管理也都有了对应的方案:
回到本文的讨论点,这里我们暂且只讨论 Vue;Vue 的核心库只关注视图层,单文件组件,其模板、逻辑和样式是内部耦合的,侧重数据和视图的同步;Vue 本身并没有对数据状态的管理进行处理,但其提供了另外一个类似 Redux 的解决方案 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 的特点其实就是下面两点:
在说集中式管理模式之前,我们可以先来想想常见的处理方式是怎样的,即每个组件维护自身的数据和状态,自给自足,分而治之;其思路大致如下:
// 渲染视图
<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
概念,组件化后,整个应用的树结构可以一目了然,可以随意添加或者移除一个组件,而不会影响其他的组件,听起来很美好;但事情并非那么完美,由于这种方式封装的组件的内部实现聚合了异步请求的数据和自身的状态,真正组装复用起来是存在一定问题的。比如:
集中式状态管理模式则以一个全局单例模式管理应用的状态,类似于全局对象,但不完全一样。
Vuex 的状态管理存储是响应式的:就是当你的组件使用到了 Vuex 的某个状态,一旦它发生改变了,所有关联的组件都会自动更新相对应的数据。
不能直接修改 Vuex 的状态:修改 Vuex 的状态唯一途径是提交(commit) mutations 来实现修改
如上图,Vuex为Vue Components建立起了一个完整的生态圈,包括开发中的API调用一环。围绕这个生态圈,简要介绍一下各模块在核心流程中的主要功能:
Vue组件接收交互行为,调用dispatch方法触发action相关处理,若页面状态需要改变,则调用commit方法提交mutation修改state,通过getters获取到state新值,重新渲染Vue Components,界面随之更新
建议类比 Redux 的流程,大体上是相同的:
相对于分治(碎片化)的状态管理,多个状态分散的跨越在不同组件交互在各个角落,每个 View 会有相对应的 Model 维护状态;而集中式管理模式则用于将分散于组件的状进行集中化管理,提供一个全局的 store 存储管理应用的状态。集中式的状态管理可以让整体的状态变化更加明晰,尤其是配合各自的 devtools。它具备以下特点:
上面提到集中式存储管理应用的所有组件的状态,而应用的状态上文已经有提到,这里大致可以分为:
这里是有歧义的,集中式存储管理应用的所有状态,按照字面意思是将所有的状态都集中式管理,也就是存到 Vuex 的全局单一 store 中,显然我们是不能这样去理解的,应该视应用场景而定的,大致也可以分为以下几种:
对于用户输入的状态,比如控制模态框的显示隐藏,我们一般在组件内处理消化;对于需要需要跨组件通信的,则可以存储在全局的 store 中,我们可以将这一类状态称之为本地状态(local state)。
对于服务端传过来的数据状态,按照大多数的实践是存储在全局的 store 中,这样可以在任意的组件中都可以使用;当然,也可以只将多组件的共享的数据存储在全局的 store 中,单个组件需要的数据内部处理消化,组件销毁时对应的数据状态也会销毁。
对于客户端的信息或者一些其他的数据状态与上面两种方式在一定程度上也是相似的。这一切看起来并没什么问题,然而细细想想,当一个应用的足够复杂时,我们该如何去设计我们的数据模型,本地共享的状态是存在 store 还是通过事件机制去处理,服务端的数据是一股脑都塞给全局的 store 存在内存里还是视应用场景而定,在 Vuex 的文档或者是 Redux 文档这都没有唯一的答案。假设服务端传过来的数据都存在 store, 那最终的 store 会有多大,这是一个值得探索的问题。那究竟什么样的数据适合存储在全局的 store 中?
另外,使用 Vuex 必须按照上述 Vuex 的工作流程去进行,定义对应的 actions
, mutations
等等,这显然是在强制约定你以相应的规则去编写你的应用,对大多数新人来说,这是繁琐的。就相当于你得到了一定的好处,那你也得相应的有所付出。
至此,我们大概讨论了由于组件化,会产生组件间相互通信数据管理的问题,对此也有相应了的解决办法;然而,并没有一种很好的方式告诉我们到底什么类型的数据适合放在单一的 store 进行管理;回到 Vuex 的定义,将数据使用 Vuex 管理的主要原因之一是解决组件间的数据共享。
所谓共享指的是,同一份数据被多处组件使用,并且要保持一定程度的同步:
故而,在开发应用时,如何设计抽象数据层,这个是没有唯一答案的。但如果明白了其间的利弊,比如独立了数据层,视图的职责就非常单一,无非就是根据订阅的数据渲染页面,视图组件间的通信就会很少,大家都会去跟数据层交互,维护一份统一的数据结构。
到这里,你应该可以确定你的应用是否应该使用 Vuex 了,如果你使用了 Vuex,那么它还有一些其他的附属产品;如下面的 vue-tool 调试工具,它可以让你对你的应用状态了如指掌,保存状态快照,历史回滚/时光旅行等等特性。
合久必分,分久必合;Vue 提倡 Single File Component
概念将单一功能进行组件化封装,而 Vuex 的设计则是将分散在各处的状态进行合并集中管理的抽象模式。利弊在上面的文章也已说明,它是一种可选的方案,你用或者不用,取决于你的应用。
注:对组件化和状态管理方面主要参考徐飞的系列文章,严重推荐深度阅读徐飞关于 《web 应用》 和 《随笔系列》 文章
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.