共计 4402 个字符,预计需要花费 12 分钟才能阅读完成。
环境搭建
因为装璜器属于一个在提案中的语法,所以不论是 node 还是浏览器,当初都没有间接反对这个语法,咱们要想应用该语法,就必须要通过 babel 将它进行一个编译转换,所以咱们须要搭建一个 babel 编译环境。
1、装置 babel 相干包
npm i @babel/cli @babel/core @babel/plugin-proposal-decorators @babel/preset-env -D
2、在我的项目根目录下创立.babelrc
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{"legacy": true}
]
]
}
根底环境搭建好当前,接下来咱们就能够纵情的应用装璜器了
类装璜器
类装璜器,顾名思义就是用来装璜整个类的,能够用来批改类的一些行为。
简略类装璜器
// src/demo01.js
// 类装璜器的简略利用
function log(target) {console.log('target:', target);
}
@log
class App {}
编译,执行
// 应用 babel 编译,将代码编译输入到 dist 文件夹
npx babel src/demo01.js -d dist
// 执行编译后的代码
node dist/demo01.js
// 编译后的代码
"use strict";
var _class;
function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");
}
}
// src/demo01.js
// 类装璜器的简略利用
function log(target) {console.log('target:', target);
}
var App = log(_class = function App() {_classCallCheck(this, App);
}) || _class;
这是 babel 编译后的源代码,其实 babel 加了一下额定的逻辑,删掉这些逻辑后,装璜器转换后的代码其实是上面这样子的:
function log(target) {console.log('target:', target);
}
class App {};
log(App);
执行输入:
target: [Function: App]
能够看到其实类装璜器就是一个函数,承受一个类作为参数,装璜器函数外部的 target 参数就是被装璜的类自身,咱们能够在装璜器函数外部对这个类进行一些批改,比方:增加动态属性,给原型增加函数等等。
带参数的类装璜器
带参数的装璜器,须要在里面再套一层承受参数的函数,像上面这样:
// src/demo02.js
function log(msg) {console.log('msg:', msg);
return function(target) {console.log('target:', target);
target.msg = msg;
}
}
@log('Jameswain')
class App {
}
console.log('App:', App);
// 编译
npx babel src/demo02.js -d dist
// 执行
node src/demo02.js
为了不便大家了解,我将 babel 编译后的代码进行了简化,删除了烦扰逻辑
// dist/demo02.js
"use strict";
function log(msg) {console.log('msg:', msg);
return function _dec (target) {console.log('target:', target);
target.msg = msg;
};
}
var _dec = log('Jameswain');
function App() {}
_dec(App);
console.log('App:', App);
执行后果:
msg: Jameswain
target: [Function: App]
App: [Function: App] {msg: 'Jameswain'}
模仿 react-redux 的 connect 实现
咱们平时开发中应用的 react-redux
就有一个 connect
装璜器,它能够把 redux 中的变量注入到指定类创立的实例中,上面咱们就通过一个例子模仿实现 connect
的性能:
// src/demo03.js => 模仿实现 react-redux 的 connect 性能
// connect 装璜器
const connect = (mapStateToProps, mapDispatchToProps) => target => {
const defaultState = {
name: 'Jameswain',
text: 'redux 默认信息'
};
// 模仿 dispatch 函数
const dispatch = payload => console.log('payload:', payload);
const {props} = target.prototype;
target.prototype.props = {...props, ...mapStateToProps(defaultState), ...mapDispatchToProps(dispatch) };
}
const mapStateToProps = state => state;
const mapDispatchToProps = dispatch => ({setUser: () => dispatch({type: 'SET_USER'})
})
@connect(mapStateToProps, mapDispatchToProps)
class App {render() {console.log('渲染函数');
}
}
const app = new App();
console.log('app:', app);
console.log('app.props:', app.props);
// 编译
npx babel src/demo03.js
// 执行
node dist/demo03.js
输入后果:
app: App {}
app.props: {name: 'Jameswain', text: 'redux 默认信息', setUser: [Function: setUser] }
从输入后果中能够看到,成果跟 react-redux
的connect
装璜器一样,返回值都被注入到 App 实例中的 props 属性中,上面咱们来看看编译进去的代码长什么样子,老规矩为了不便大家了解,我删除掉 babel 的烦扰代码,只保留外围逻辑:
// dist/demo03.js
"use strict";
// 模仿实现 react-redux 的 connect 性能
// connect 装璜器
function connect(mapStateToProps, mapDispatchToProps) {return function (target) {
var defaultState = {
name: 'Jameswain',
text: 'redux 默认信息'
};
function dispatch(payload) {return console.log('payload:', payload);
};
var props = target.prototype.props;
target.prototype.props = {...props, ...mapStateToProps(defaultState), ...mapDispatchToProps(dispatch) };
};
};
function mapStateToProps(state) {return state;};
function mapDispatchToProps(dispatch) {
return {setUser: function setUser() {
return dispatch({type: 'SET_USER'});
}
};
};
function App() {}
App.prototype.render = function() {console.log('渲染函数');
}
connect(mapStateToProps, mapDispatchToProps)(App);
var app = new App();
console.log('app:', app);
console.log('app.props:', app.props);
比照编译后的代码,能够发现其实装璜器就是一个语法糖而已,实现截然不同,只是调用的形式不一样。
// 装璜器用法
@connect(mapStateToProps, mapDispatchToProps)
class App {}
// 函数式用法
@connect(mapStateToProps, mapDispatchToProps)(class App {})
装璜器的执行程序
一个类中能够有多个装璜器,装璜器的执行程序是:从下往上,从右往左执行。比方上面这个例子:
// src/demo04.js 装璜器的执行程序
function log(target) {console.log('log:', target);
}
function connect(target) {console.log('connect:', target);
}
function withRouter(target) {console.log('withRouter:', target);
}
@log
@withRouter
@connect
class App {}
// 编译
npx babel src/demo04.js -d dist
// 执行
node dist/demo04.js
运行后果:
# 从下往上执行
connect: [Function: App]
withRouter: [Function: App]
log: [Function: App]
编译后的代码:
// src/demo04.js 装璜器的执行程序
"use strict";
function log(target) {console.log('log:', target);
}
function connect(target) {console.log('connect:', target);
}
function withRouter(target) {console.log('withRouter:', target);
}
var _class;
var App = log(_class = withRouter(_class = connect(_class = function App() {}) || _class) || _class) || _class;
从编译后的代码中能够看出,多个装璜器其实就是一层层的函数嵌套,从里往外执行,然而显然是装璜逻辑更清晰,易读。