环境搭建

因为装璜器属于一个在提案中的语法,所以不论是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-reduxconnect装璜器一样,返回值都被注入到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;

从编译后的代码中能够看出,多个装璜器其实就是一层层的函数嵌套,从里往外执行,然而显然是装璜逻辑更清晰,易读。