环境搭建
因为装璜器属于一个在提案中的语法,所以不论是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);}@logclass 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.jsfunction 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: Jameswaintarget: [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@connectclass 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;
从编译后的代码中能够看出,多个装璜器其实就是一层层的函数嵌套,从里往外执行,然而显然是装璜逻辑更清晰,易读。