mini mvvm

自用,bug较多,if和for指令不能使用

当前为 2022-05-04 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/444466/1047054/mini%20mvvm.js

  1. // ==UserScript==
  2. // @name mini mvvm
  3. // @namespace Violentmonkey Scripts
  4. // @match *://*/*
  5. // @grant none
  6. // @version 0.0.1
  7. // @author -
  8. // @description 自用,bug较多,if和for指令不能使用
  9. // ==/UserScript==
  10. function* getSequence() {
  11. let i = 0;
  12. while (true) {
  13. yield (i = i + 1);
  14. }
  15. }
  16. let sequence = getSequence();
  17. function getId(name) {
  18. return `${name ? name : 'none'}.${new Date().getTime()}.${Math.floor(Math.random() * 10000)}.${sequence.next().value}`;
  19. }
  20. class Dep {
  21. constructor(name) {
  22. this.subs = [];
  23. this.id = getId(name);
  24. }
  25. delete() {
  26. if (this.subs.length < 1)
  27. return;
  28. this.notify();
  29. this.subs.forEach((sub) => {
  30. sub.removeDep(this);
  31. });
  32. this.subs.length = 0;
  33. }
  34. addSub(sub) {
  35. this.subs.push(sub);
  36. }
  37. depend() {
  38. Dep.target.addDep(this);
  39. }
  40. notify() {
  41. this.subs.forEach((sub) => {
  42. sub.update();
  43. });
  44. }
  45. }
  46. Dep.target = null;
  47.  
  48. const extend = (a, b) => {
  49. for (const key in b) {
  50. a[key] = b[key];
  51. }
  52. return a;
  53. };
  54. const isFunction = (val) => typeof val === 'function';
  55. const NOOP = () => { };
  56. const objectToString = Object.prototype.toString;
  57. const toTypeString = (value) => objectToString.call(value);
  58. const isPlainObject = (val) => toTypeString(val) === '[object Object]';
  59. const hasOwnProperty = Object.prototype.hasOwnProperty;
  60. const hasOwn = (val, key) => hasOwnProperty.call(val, key);
  61. function toArray(nodes) {
  62. return [].slice.call(nodes);
  63. }
  64. const unique = (arr) => Array.from(new Set(arr));
  65.  
  66. function observe(value, vm) {
  67. if (!value || typeof value !== 'object')
  68. return value;
  69. if(value.__ob__)
  70. return value
  71. return new Observer(value, vm).proxy;
  72. }
  73. class Observer {
  74. constructor(data, vm) {
  75. Object.keys(data).forEach((key) => {
  76. data[key] = observe(data[key], vm);
  77. });
  78. this.dep = new Dep('data');
  79. Object.defineProperty(data, '__ob__', {
  80. configurable: false,
  81. enumerable: false,
  82. value:this
  83. })
  84. this.proxy = new Proxy(data, {
  85. get: (target, key, receiver) => {
  86. const result = Reflect.get(target, key, receiver)
  87. if (Dep.target){
  88. if(isPlainObject(result) && result.__ob__)
  89. result.__ob__.dep.depend()
  90. else
  91. this.dep.depend()
  92. }
  93. return result;
  94. },
  95. set: (target, key, newValue, receiver) => {
  96. const result = Reflect.set(target, key, observe(newValue), receiver);
  97. this.dep.notify();
  98. return result;
  99. },
  100. deleteProperty: (target, key) => {
  101. const childObj = target[key];
  102. let result = false;
  103. if (isPlainObject(childObj) && hasOwn(childObj, '__ob__')) {
  104. let ob = childObj['__ob__'];
  105. ob.dep.delete();
  106. ob = null;
  107. result = Reflect.deleteProperty(target, key);
  108. this.dep.notify();
  109. }
  110. return result;
  111. },
  112. });
  113. }
  114. }
  115.  
  116. class EventEmitter {
  117. constructor(scope) {
  118. this._events = new Map();
  119. if (scope)
  120. this._scope = scope;
  121. }
  122. has(eventName){
  123. return this._events.has(eventName)
  124. }
  125. on(eventName, callback) {
  126. if (!this._events.has(eventName))
  127. this._events.set(eventName, []);
  128. this._events.get(eventName).push(callback);
  129. }
  130. emit(eventName, value) {
  131. if (!this._events.has(eventName))
  132. return;
  133. this._events.get(eventName).forEach((callback) => {
  134. if (isFunction(callback))
  135. callback(value);
  136. });
  137. }
  138. off(eventName, callback) {
  139. if (callback) {
  140. this._events.set(eventName, this._events.get(eventName).filter((cb) => {
  141. if (cb === callback || cb.originFunction === callback)
  142. return false;
  143. }));
  144. }
  145. else {
  146. this._events.delete(eventName);
  147. }
  148. }
  149. once(eventName, callback) {
  150. const self = this;
  151. const onceCallback = function () {
  152. self.off(eventName, onceCallback);
  153. callback.apply(self, arguments);
  154. };
  155. onceCallback.originFunction = callback;
  156. this.on(eventName, onceCallback);
  157. }
  158. }
  159.  
  160. var EventLoop;
  161. (function (EventLoop) {
  162. const callbacks = [];
  163. const p = Promise.resolve();
  164. let pending = false;
  165. let useMacroTask = false;
  166. function flushCallbacks() {
  167. pending = false;
  168. const copies = callbacks.slice(0);
  169. callbacks.length = 0;
  170. copies.forEach((fn) => fn());
  171. }
  172. EventLoop.flushCallbacks = flushCallbacks;
  173. const macroTimerFunction = () => {
  174. setTimeout(flushCallbacks, 0);
  175. };
  176. const microTimerFunction = () => {
  177. p.then(flushCallbacks);
  178. };
  179. function withMacroTask(fn) {
  180. return (fn._withTask ||
  181. (fn._withTask = function () {
  182. useMacroTask = true;
  183. const res = fn.apply(null, arguments);
  184. useMacroTask = false;
  185. return res;
  186. }));
  187. }
  188. EventLoop.withMacroTask = withMacroTask;
  189. function nextTick(context, callback) {
  190. let _resolve;
  191. callbacks.push(() => {
  192. if (callback)
  193. callback.call(context);
  194. else if (_resolve)
  195. _resolve(context);
  196. });
  197. if (!pending) {
  198. pending = true;
  199. if (useMacroTask)
  200. macroTimerFunction();
  201. else
  202. microTimerFunction();
  203. }
  204. if (!callback)
  205. return new Promise((resolve) => {
  206. _resolve = resolve;
  207. });
  208. }
  209. EventLoop.nextTick = nextTick;
  210. })(EventLoop || (EventLoop = {}));
  211.  
  212. function getApplyFunction(fn, scope) {
  213. const func = function () {
  214. fn.apply(scope, arguments);
  215. };
  216. return func;
  217. }
  218. const createVM = (options = {}) => new MVVM(extend(options, {
  219. element: options.element ? options.element : document.body
  220. }));
  221. class MVVM {
  222. constructor(options = {}) {
  223. this.$event = new EventEmitter(this);
  224. this.$children = {};
  225. this.$refs = {};
  226. this.$on = getApplyFunction(this.$event.on, this.$event);
  227. this.$emit = getApplyFunction(this.$event.emit, this.$event);
  228. this.$off = getApplyFunction(this.$event.off, this.$event);
  229. this.$once = getApplyFunction(this.$event.once, this.$event);
  230. this.$options = options;
  231. this.components = options.components;
  232. MVVM.cid += 1;
  233. this.cid = MVVM.cid;
  234. this._init();
  235. if (this.$options.element)
  236. this.compile(this.$options.element);
  237. }
  238. $watch(key, cb) {
  239. new Watcher(this, key, cb);
  240. }
  241. $nextTick(callback) {
  242. if (callback)
  243. return EventLoop.nextTick(this, callback);
  244. return EventLoop.nextTick(this);
  245. }
  246. use(fn) {
  247. fn.call(this, this);
  248. return this;
  249. }
  250. compile(element) {
  251. this.$compile = new Compile(element, this);
  252. this.$emit('mounted');
  253. }
  254. _init() {
  255. this._initMethods();
  256. this._initLifecycle();
  257. this.$emit('beforeCreate');
  258. this._initData();
  259. this._initComputed();
  260. this._initWatch();
  261. this.$emit('created');
  262. }
  263. _initMethods() {
  264. let methods = this.$options.methods;
  265. if (typeof methods !== 'object')
  266. return;
  267. Object.keys(methods).forEach((key) => {
  268. let object = methods[key];
  269. if (!isFunction(object))
  270. return;
  271. if (this[key])
  272. return;
  273. this[key] = object;
  274. });
  275. }
  276. _initLifecycle() {
  277. this.$options.beforeCreate &&
  278. this.$on('beforeCreate', this.$options.beforeCreate.bind(this));
  279. this.$options.created && this.$on('created', this.$options.created.bind(this));
  280. this.$options.beforeMount &&
  281. this.$on('beforeMount', this.$options.beforeMount);
  282. this.$options.mounted && this.$on('mounted', this.$options.mounted.bind(this));
  283. this.$options.beforeUpdate &&
  284. this.$on('beforeUpdate', this.$options.beforeUpdate);
  285. this.$options.updated && this.$on('updated', this.$options.updated.bind(this));
  286. }
  287. _initData() {
  288. const data = this.$options.data;
  289. this.$data = isFunction(data) ? data.call(this) : data;
  290. Object.keys(this.$data).forEach((key) => Object.defineProperty(this, key, {
  291. configurable: false,
  292. enumerable: true,
  293. get: () => {
  294. return this.$data[key];
  295. },
  296. set: (newVal) => {
  297. this.$data[key] = newVal;
  298. },
  299. }));
  300. this.$data = observe(this.$data, this);
  301. }
  302. _initComputed() {
  303. let computed = this.$options.computed;
  304. if (!isPlainObject(computed))
  305. return;
  306. Object.keys(computed).forEach((key) => {
  307. let object = computed[key];
  308. Object.defineProperty(this, key, {
  309. get: isFunction(object)
  310. ? object
  311. : 'get' in object
  312. ? object.get
  313. : NOOP,
  314. set: isFunction(object)
  315. ? object
  316. : 'set' in object
  317. ? object.set
  318. : NOOP,
  319. });
  320. });
  321. }
  322. _initWatch() {
  323. let watch = this.$options.watch;
  324. if (typeof watch !== 'object')
  325. return;
  326. Object.keys(watch).forEach((key) => {
  327. let object = watch[key];
  328. if (!isFunction(object))
  329. return;
  330. this.$watch(key, object);
  331. });
  332. }
  333. }
  334. MVVM.cid = 0;
  335. function getVMVal(vm, exp) {
  336. let temp;
  337. exp.split('.').forEach((k, i) => {
  338. if (i === 0)
  339. temp = vm[k];
  340. else
  341. temp = temp[k];
  342. });
  343. return temp;
  344. }
  345. function setVMVal(vm, exp, value) {
  346. let temp;
  347. let exps = exp.split('.');
  348. if (exps.length === 1)
  349. vm[exps[0]] = value;
  350. else
  351. exps.forEach((k, i, exps) => {
  352. if (i === 0)
  353. temp = vm[k];
  354. else if (i < exps.length - 1)
  355. temp = temp[k];
  356. else if (i === exps.length - 1)
  357. temp[k] = value;
  358. });
  359. }
  360.  
  361. function parseGetter(exp) {
  362. return (vm) => getVMVal(vm, exp);
  363. }
  364. class Watcher {
  365. constructor(vm, expOrFn, callback,deep = false) {
  366. this.callback = callback;
  367. this.vm = vm;
  368. this.deep = deep
  369. this._depIds = {};
  370. if (isFunction(expOrFn))
  371. this._getter = expOrFn;
  372. else
  373. this._getter = parseGetter(expOrFn.trim());
  374. this.value = this.get();
  375. }
  376. update() {
  377. let newVal = this.get();
  378. let oldVal = this.value;
  379. if (newVal === oldVal && !(this.deep && isPlainObject(newVal) && isPlainObject(oldVal))) return
  380. this.value = newVal;
  381. this.callback.call(this.vm, newVal, oldVal);
  382. }
  383. removeDep(dep) {
  384. delete this._depIds[dep.id];
  385. }
  386. addDep(dep) {
  387. if (!hasOwn(this._depIds, dep.id)) {
  388. dep.addSub(this);
  389. this._depIds[dep.id] = dep;
  390. }
  391. }
  392. get() {
  393. Dep.target = this;
  394. let value = this._getter.call(this.vm, this.vm);
  395. Dep.target = null;
  396. return value;
  397. }
  398. }
  399.  
  400. class ElementUtility {
  401. static fragment(el) {
  402. let fragment = document.createDocumentFragment(), child;
  403. while ((child = el.firstChild))
  404. fragment.appendChild(child);
  405. return fragment;
  406. }
  407. static parseHTML(html) {
  408. const domParser = new DOMParser();
  409. let temp = domParser.parseFromString(html, 'text/html');
  410. return temp.body.children;
  411. }
  412. static isElementNode(node) {
  413. if (node instanceof Element)
  414. return node.nodeType == 1;
  415. return false;
  416. }
  417. static isTextNode(node) {
  418. if (node instanceof Text)
  419. return node.nodeType == 3;
  420. return false;
  421. }
  422. static text(node, value) {
  423. if (typeof value === 'number')
  424. value = String(value);
  425. node.textContent = value ? value : '';
  426. }
  427. static html(node, value) {
  428. if (typeof value === 'number')
  429. value = String(value);
  430. node.innerHTML = value ? value : '';
  431. }
  432. static class(node, value, oldValue) {
  433. let className = node.className;
  434. className = className.replace(oldValue, '').replace(/\s$/, '');
  435. let space = className && String(value) ? ' ' : '';
  436. node.className = className + space + value;
  437. }
  438. static model(node, newValue) {
  439. if (typeof newValue === 'number')
  440. newValue = String(newValue);
  441. node.value = newValue ? newValue : '';
  442. }
  443. static style(node, newValue, oldValue) {
  444. if (!oldValue)
  445. oldValue = {};
  446. if (!newValue)
  447. newValue = {};
  448. const keys = Object.keys(oldValue).concat(Object.keys(newValue));
  449. unique(keys).forEach((key) => {
  450. if (hasOwn(oldValue, key) && hasOwn(newValue, key)) {
  451. if (oldValue[key] != newValue[key])
  452. node.style.setProperty(key, newValue[key]);
  453. }
  454. else if (hasOwn(newValue, key))
  455. node.style.setProperty(key, newValue[key]);
  456. else
  457. node.style.removeProperty(key);
  458. });
  459. }
  460. static display(node, newValue, oldValue) {
  461. let func = (val) => {
  462. return {
  463. display: val ? 'block' : 'none',
  464. };
  465. };
  466. ElementUtility.style(node, func(newValue), null);
  467. }
  468. }
  469.  
  470. class MVVMComponent extends MVVM {
  471. constructor(options) {
  472. super(options);
  473. this.$template = options.template || '';
  474. if (options.parent)
  475. this.$parent = options.parent;
  476. }
  477. $mount(element) {
  478. this.compile(element);
  479. }
  480. }
  481.  
  482. const parseAnyDirectiveFunction = (parseString) => {
  483. return (dir) => dir.indexOf(parseString) == 0;
  484. };
  485. const isDirective = parseAnyDirectiveFunction('v-');
  486. const isEventDirective = parseAnyDirectiveFunction('on');
  487. const isTextDirective = parseAnyDirectiveFunction('text');
  488. const isHtmlDirective = parseAnyDirectiveFunction('html');
  489. const isModelDirective = parseAnyDirectiveFunction('model');
  490. const isClassDirective = parseAnyDirectiveFunction('class');
  491. const isStyleDirective = parseAnyDirectiveFunction('style');
  492. const isShowDirective = parseAnyDirectiveFunction('show');
  493. const isRefDirective = parseAnyDirectiveFunction('ref');
  494. const isForDirective = parseAnyDirectiveFunction('for');
  495.  
  496. function bindWatcher(node, vm, exp, updater) {
  497. let __for__ = node['__for__'];
  498. let val;
  499. if (__for__ && __for__[exp])
  500. val = __for__[exp]
  501. else
  502. val = getVMVal(vm, exp);
  503. updater && updater(node, val);
  504. new Watcher(vm, exp, (newValue, oldValue) => {
  505. if (newValue === oldValue)
  506. return;
  507. updater && updater(node, newValue, oldValue);
  508. });
  509. }
  510. function eventHandler(node, vm, exp, eventType) {
  511. let fn = vm.$options.methods && vm.$options.methods[exp];
  512. if (eventType && fn) {
  513. if(node instanceof MVVMComponent)
  514. node.$on(eventType,fn.bind(vm))
  515. else
  516. node.addEventListener(eventType, fn.bind(vm), false);
  517. }
  518. }
  519. function vFor(node, vm, exp, c) {
  520. let reg = /\((.*)\)/;
  521. let item, index, list;
  522. if (reg.test(exp)) {
  523. const arr = RegExp.$1.trim().split(',');
  524. item = arr[0];
  525. index = arr[1];
  526. let rightString = RegExp.rightContext.trim();
  527. let rarr = rightString.split(' ');
  528. list = rarr[1];
  529. if (rarr[0] !== 'in')
  530. return;
  531. let val = getVMVal(vm, list);
  532. let children = [];
  533. toArray(node.children).forEach((element) => {
  534. children.push(element.cloneNode(true));
  535. node.removeChild(element);
  536. });
  537. for (let i = 0; i < val.length; i++) {
  538. children.forEach((element) => {
  539. let newNode = element.cloneNode(true);
  540. newNode.__for__ = {
  541. [item]: val[i],
  542. [index]: i
  543. };
  544. node.appendChild(newNode);
  545. c.compileElement(node);
  546. });
  547. }
  548. }
  549. }
  550. function forHandler(node, vm, exp, c) {
  551. vFor(node, vm, exp, c);
  552. new Watcher(vm, exp, (newValue, oldValue) => {
  553. if (newValue === oldValue)
  554. return;
  555. vFor(node, vm, exp, c);
  556. });
  557. }
  558. class Compile {
  559. constructor(el, vm) {
  560. this.slotCallback = [];
  561. this.$vm = vm;
  562. this.$el = ElementUtility.isElementNode(el)
  563. ? el
  564. : document.querySelector(el);
  565. this._init();
  566. }
  567. _init() {
  568. if (this.$vm instanceof MVVMComponent) {
  569. this.$slot = ElementUtility.fragment(this.$el);
  570. this.$fragment = this.parseComponentTemplate(this.$vm.$template);
  571. this.$vm.$el = this.$el;
  572. this.$vm.$emit('beforeMount');
  573. this.compileElement(this.$fragment);
  574. this.$el.parentNode.replaceChild(this.$fragment, this.$el);
  575. }
  576. else {
  577. this.$fragment = ElementUtility.fragment(this.$el);
  578. this.$vm.$el = this.$el;
  579. this.$vm.$emit('beforeMount');
  580. this.compileElement(this.$fragment);
  581. this.$el.appendChild(this.$fragment);
  582. }
  583. Object.entries(this.$vm.$children).forEach(([key, child]) => {
  584. const slotCallback = child.$compile.slotCallback;
  585. if (slotCallback.length < 1)
  586. return;
  587. slotCallback.forEach((fn) => {
  588. fn(this);
  589. });
  590. });
  591. }
  592. isSlot(node) {
  593. if (node.tagName === 'SLOT')
  594. return true;
  595. return false;
  596. }
  597. compileSlotElement(slot) {
  598. if (!(this.$vm instanceof MVVMComponent))
  599. return;
  600. if (this.$slot.children.length === 0) {
  601. slot.parentNode.removeChild(slot);
  602. return;
  603. }
  604. this.slotCallback.push(c => {
  605. c.compileElement(this.$slot);
  606. slot.parentNode.replaceChild(this.$slot, slot);
  607. });
  608. }
  609. parseComponentTemplate(templateHTML) {
  610. let element = ElementUtility.parseHTML(templateHTML);
  611. const template = document.createElement('template');
  612. if (element.length) {
  613. if (element.length === 1) {
  614. if (element[0].tagName.toLowerCase() !== 'template')
  615. template.appendChild(element[0]);
  616. }
  617. else
  618. toArray(element).forEach((child) => {
  619. template.appendChild(child);
  620. });
  621. }
  622. return ElementUtility.fragment(template);
  623. }
  624. parseTemplate(leftString, rightString) {
  625. return (node, newValue, oldValue) => {
  626. const str = leftString + newValue + rightString;
  627. ElementUtility.text(node, str);
  628. };
  629. }
  630. compileElement(el) {
  631. let childNodes = [];
  632. // slice
  633. el.childNodes.forEach(node=>{
  634. childNodes.push(node)
  635. })
  636. childNodes.forEach((node) => {
  637. if (el['__for__'])
  638. node['__for__'] = el['__for__'];
  639. let reg = /\{\{(.*)\}\}/;
  640. if (ElementUtility.isElementNode(node)) {
  641. if (this.isComponent(node))
  642. this.compileComponent(node.tagName.toLowerCase(), node);
  643. else if (this.isSlot(node))
  644. this.compileSlotElement(node);
  645. else
  646. this.compile(node);
  647. }
  648. else if (ElementUtility.isTextNode(node) &&
  649. reg.test(node.textContent))
  650. bindWatcher(node, this.$vm, RegExp.$1.trim(), this.parseTemplate(RegExp.leftContext, RegExp.rightContext));
  651. if (node.childNodes && node.childNodes.length)
  652. this.compileElement(node);
  653. });
  654. }
  655. compile(node) {
  656. let nodeAttrs = node.attributes;
  657. toArray(nodeAttrs).forEach((attr) => {
  658. let attrName = attr.name;
  659. if (!isDirective(attrName)){
  660. if (attrName.startsWith('[') && attrName.endsWith(']')) {
  661. node.removeAttribute(attrName)
  662. let realAttrName = attrName.replace('[','')
  663. realAttrName = realAttrName.replace(']','')
  664. bindWatcher(node,this.$vm,attr.value,(node,newVal,oldVal)=>{
  665. node.setAttribute(realAttrName,newVal)
  666. })
  667. }
  668. return;
  669. }
  670. let dir = attrName.substring(2);
  671. let suffix = dir.split(':')[1];
  672. let exp = attr.value || suffix;
  673. if (isEventDirective(dir))
  674. eventHandler(node, this.$vm, exp, suffix);
  675. else if (isTextDirective(dir))
  676. bindWatcher(node, this.$vm, exp, ElementUtility.text);
  677. else if (isHtmlDirective(dir))
  678. bindWatcher(node, this.$vm, exp, ElementUtility.html);
  679. else if (isClassDirective(dir))
  680. bindWatcher(node, this.$vm, exp, ElementUtility.class);
  681. else if (isModelDirective(dir)) {
  682. bindWatcher(node, this.$vm, exp, ElementUtility.model);
  683. let val = getVMVal(this.$vm, exp);
  684. node.addEventListener('input', (e) => {
  685. let target = e.target;
  686. let newValue = target.value;
  687. if (val === newValue)
  688. return;
  689. setVMVal(this.$vm, exp, newValue);
  690. val = newValue;
  691. });
  692. }
  693. else if (isStyleDirective(dir))
  694. bindWatcher(node, this.$vm, exp, ElementUtility.style);
  695. else if (isShowDirective(dir))
  696. bindWatcher(node, this.$vm, exp, ElementUtility.display);
  697. else if (isRefDirective(dir))
  698. this.$vm.$refs[exp] = node;
  699. else if (isForDirective(dir))
  700. forHandler(node, this.$vm, exp, this);
  701. node.removeAttribute(attrName);
  702. });
  703. }
  704. isComponent(node) {
  705. const tagName = node.tagName.toLowerCase();
  706. if (!/^[(a-zA-Z)-]*$/.test(tagName))
  707. return false;
  708. if (this.$vm.components && hasOwn(this.$vm.components, tagName))
  709. return true;
  710. return false;
  711. }
  712. compileComponent(componentName, node) {
  713. const attributes = []
  714. toArray(node.attributes).forEach((attr) => {
  715. attributes.push(attr)
  716. })
  717. const componentOptions = this.$vm.components[componentName];
  718. const component = new MVVMComponent(extend(componentOptions, {
  719. parent: this.$vm
  720. }));
  721. component.$mount(node);
  722. this.$vm.$children[componentName] = component;
  723. attributes.forEach(attr=>{
  724. let attrName = attr.name;
  725. if (!isDirective(attrName))
  726. return;
  727. let dir = attrName.substring(2);
  728. let suffix = dir.split(':')[1];
  729. let exp = attr.value || suffix;
  730. if (isEventDirective(dir))
  731. eventHandler(component, this.$vm, exp, suffix);
  732. else if (isRefDirective(dir))
  733. this.$vm.$refs[exp] = component;
  734. })
  735. }
  736. }