亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

從 VantComponent 談 小程序維護(hù)

worldligang / 2301人閱讀

摘要:不多廢話,先說(shuō)結(jié)論小程序組件寫法這里就不再介紹。在官方文檔中,我們可以看到使用構(gòu)造器構(gòu)造頁(yè)面事實(shí)上,小程序的頁(yè)面也可以視為自定義組件。經(jīng)過(guò)一番測(cè)試,得出結(jié)果為為了簡(jiǎn)便。畢竟官方標(biāo)準(zhǔn),不用擔(dān)心其他一系列后續(xù)問(wèn)題。

在開(kāi)發(fā)小程序的時(shí)候,我們總是期望用以往的技術(shù)規(guī)范和語(yǔ)法特點(diǎn)來(lái)書寫當(dāng)前的小程序,所以才會(huì)有各色的小程序框架,例如 mpvue、taro 等這些編譯型框架。當(dāng)然這些框架本身對(duì)于新開(kāi)發(fā)的項(xiàng)目是有所幫助。而對(duì)于老項(xiàng)目,我們又想要利用 vue 的語(yǔ)法特性進(jìn)行維護(hù),又該如何呢?
在此我研究了一下youzan的 vant-weapp。而發(fā)現(xiàn)該項(xiàng)目中的組件是如此編寫的。

import { VantComponent } from "../common/component";

VantComponent({
  mixins: [],
  props: {
    name: String,
    size: String
  },
  // 可以使用 watch 來(lái)監(jiān)控 props 變化
  // 其實(shí)就是把properties中的observer提取出來(lái)
  watch: {
    name(newVal) {
       ...
    },
    // 可以直接使用字符串 代替函數(shù)調(diào)用
    size: "changeSize"
  },
  // 使用計(jì)算屬性 來(lái) 獲取數(shù)據(jù),可以在 wxml直接使用
  computed: {
    bigSize() {
      return this.data.size + 100
    }
  },
  data: {
    size: 0
  },
  methods: {
    onClick() {
      this.$emit("click");
    },
    changeSize(size) {
       // 使用set
       this.set(size)
    }
  },

  // 對(duì)應(yīng)小程序組件 created 周期
  beforeCreate() {},

  // 對(duì)應(yīng)小程序組件 attached 周期
  created() {},

  // 對(duì)應(yīng)小程序組件 ready 周期
  mounted() {},

  // 對(duì)應(yīng)小程序組件  detached 周期
  destroyed: {}
});

居然發(fā)現(xiàn)該組件寫法整體上類似于 Vue 語(yǔ)法。而本身卻沒(méi)有任何編譯??磥?lái)問(wèn)題是出在了導(dǎo)入的 VantComponet 這個(gè)方法上。下面我們開(kāi)始詳細(xì)介紹一下如何利用 VantComponet 來(lái)對(duì)老項(xiàng)目進(jìn)行維護(hù)。

TLDR (不多廢話,先說(shuō)結(jié)論)

小程序組件寫法這里就不再介紹。這里我們給出利用 VantComponent 寫 Page 的代碼風(fēng)格。

import { VantComponent } from "../common/component"; 

VantComponent({
  mixins: [],
  props: {
    a: String,
    b: Number
  },
  // 在頁(yè)面這里 watch 基本上是沒(méi)有作用了,因?yàn)橹蛔隽藀rops 變化的watch,page不會(huì)出現(xiàn) props 變化
  // 后面會(huì)詳細(xì)說(shuō)明為何
  watch: {},
  // 計(jì)算屬性仍舊可用
  computed: {
    d() {
      return c++
    }
  },
  methods: {
    onLoad() {}
  },
  created() {},
  // 其他組件生命周期
})

這里你可能感到疑惑,VantComponet 不是對(duì)組件 Component 生效的嗎?怎么會(huì)對(duì)頁(yè)面 Page 生效呢。事實(shí)上,我們是可以使用組件來(lái)構(gòu)造小程序頁(yè)面的。
在官方文檔中,我們可以看到 使用 Component 構(gòu)造器構(gòu)造頁(yè)面
事實(shí)上,小程序的頁(yè)面也可以視為自定義組件。因而,頁(yè)面也可以使用 Component 構(gòu)造器構(gòu)造,擁有與普通組件一樣的定義段與實(shí)例方法。代碼編寫如下:

Component({
    // 可以使用組件的 behaviors 機(jī)制,雖然 React 覺(jué)得 mixins 并不是一個(gè)很好的方案
    // 但是在某種程度該方案的確可以復(fù)用相同的邏輯代碼
    behaviors: [myBehavior],
   
    // 對(duì)應(yīng)于page的options,與此本身是有類型的,而從options 取得數(shù)據(jù)均為 string類型
    // 訪問(wèn) 頁(yè)面 /pages/index/index?paramA=123¶mB=xyz 
    // 如果聲明有屬性 paramA 或 paramB ,則它們會(huì)被賦值為 123 或 xyz,而不是 string類型
    properties: {
        paramA: Number,
        paramB: String,
    },
    methods: {
        // onLoad 不需要 option
        // 但是頁(yè)面級(jí)別的生命周期卻只能寫道 methods中來(lái)
        onLoad() {
            this.data.paramA // 頁(yè)面參數(shù) paramA 的值 123
            this.data.paramB // 頁(yè)面參數(shù) paramB 的值 ’xyz’
        }
    }

})

那么組件的生命周期和頁(yè)面的生命周期又是怎么對(duì)應(yīng)的呢。經(jīng)過(guò)一番測(cè)試,得出結(jié)果為: (為了簡(jiǎn)便。只會(huì)列出 重要的的生命周期)

// 組件實(shí)例被創(chuàng)建 到 組件實(shí)例進(jìn)入頁(yè)面節(jié)點(diǎn)樹(shù)
component created -> component attched -> 
// 頁(yè)面頁(yè)面加載 到  組件在視圖層布局完成
page onLoad -> component ready -> 
// 頁(yè)面卸載 到 組件實(shí)例被從頁(yè)面節(jié)點(diǎn)樹(shù)移除
page OnUnload -> component detached

當(dāng)然 我們重點(diǎn)不是在 onload 和 onunload 中間的狀態(tài),因?yàn)橹虚g狀態(tài)的時(shí)候,我們可以在頁(yè)面中使用頁(yè)面生命周期來(lái)操作更好。
某些時(shí)候我們的一些初始化代碼不應(yīng)該放在 onload 里面,我們可以考慮放在 component create 進(jìn)行操作,甚至可以利用 behaviors 來(lái)復(fù)用初始化代碼。
某種方面來(lái)說(shuō),如果不需要 Vue 風(fēng)格,我們?cè)诶享?xiàng)目中直接利用 Component 代替 Page 也不失為一個(gè)不錯(cuò)的維護(hù)方案。畢竟官方標(biāo)準(zhǔn),不用擔(dān)心其他一系列后續(xù)問(wèn)題。

VantComponent 源碼解析 VantComponent

此時(shí),我們對(duì) VantComponent 開(kāi)始進(jìn)行解析

// 賦值,根據(jù) map 的 key 和 value 來(lái)進(jìn)行操作
function mapKeys(source: object, target: object, map: object) {
  Object.keys(map).forEach(key => {
    if (source[key]) {
      // 目標(biāo)對(duì)象 的 map[key] 對(duì)應(yīng) 源數(shù)據(jù)對(duì)象的 key
      target[map[key]] = source[key];
    }
  });
}

// ts代碼,也就是 泛型
function VantComponent(
  vantOptions: VantComponentOptions<
    Data,
    Props,
    Watch,
    Methods,
    Computed,
    CombinedComponentInstance
  > = {}
): void {
  const options: any = {};
  // 用function 來(lái)拷貝 新的數(shù)據(jù),也就是我們可以用的 Vue 風(fēng)格
  mapKeys(vantOptions, options, {
    data: "data",
    props: "properties",
    mixins: "behaviors",
    methods: "methods",
    beforeCreate: "created",
    created: "attached",
    mounted: "ready",
    relations: "relations",
    destroyed: "detached",
    classes: "externalClasses"
  });

  // 對(duì)組件間關(guān)系進(jìn)行編輯,但是page不需要,可以刪除
  const { relation } = vantOptions;
  if (relation) {
    options.relations = Object.assign(options.relations || {}, {
      [`../${relation.name}/index`]: relation
    });
  }

  // 對(duì)組件默認(rèn)添加 externalClasses,但是page不需要,可以刪除
  // add default externalClasses
  options.externalClasses = options.externalClasses || [];
  options.externalClasses.push("custom-class");

  // 對(duì)組件默認(rèn)添加 basic,封裝了 $emit 和小程序節(jié)點(diǎn)查詢方法,可以刪除
  // add default behaviors
  options.behaviors = options.behaviors || [];
  options.behaviors.push(basic);

  // map field to form-field behavior
  // 默認(rèn)添加 內(nèi)置 behavior  wx://form-field
  // 它使得這個(gè)自定義組件有類似于表單控件的行為。
  // 可以研究下文給出的 內(nèi)置behaviors
  if (vantOptions.field) {
    options.behaviors.push("wx://form-field");
  }

  // add default options
  // 添加組件默認(rèn)配置,多slot
  options.options = {
    multipleSlots: true,// 在組件定義時(shí)的選項(xiàng)中啟用多slot支持
    // 如果這個(gè) Component 構(gòu)造器用于構(gòu)造頁(yè)面 ,則默認(rèn)值為 shared
    // 組件的apply-shared,可以研究下文給出的 組件樣式隔離
    addGlobalClass: true 
  };

  // 監(jiān)控 vantOptions
  observe(vantOptions, options);

  // 把當(dāng)前重新配置的options 放入Component
  Component(options);
}

內(nèi)置behaviors
組件樣式隔離

basic behaviors

剛剛我們談到 basic behaviors,代碼如下所示

export const basic = Behavior({
  methods: {
    // 調(diào)用 $emit組件 實(shí)際上是使用了 triggerEvent
    $emit() {
      this.triggerEvent.apply(this, arguments);
    },

    // 封裝 程序節(jié)點(diǎn)查詢
    getRect(selector: string, all: boolean) {
      return new Promise(resolve => {
        wx.createSelectorQuery()
          .in(this)[all ? "selectAll" : "select"](selector)
          .boundingClientRect(rect => {
            if (all && Array.isArray(rect) && rect.length) {
              resolve(rect);
            }

            if (!all && rect) {
              resolve(rect);
            }
          })
          .exec();
      });
    }
  }
});
observe

小程序 watch 和 computed的 代碼解析

export function observe(vantOptions, options) {
  // 從傳入的 option中得到 watch computed  
  const { watch, computed } = vantOptions;

  // 添加  behavior
  options.behaviors.push(behavior);

  /// 如果有 watch 對(duì)象
  if (watch) {
    const props = options.properties || {};
    // 例如: 
    // props: {
    //   a: String
    // },
    // watch: {
    //   a(val) {
    //     // 每次val變化時(shí)候打印
    //     consol.log(val)
    //   }
    } 
    Object.keys(watch).forEach(key => {
      
      // watch只會(huì)對(duì)prop中的數(shù)據(jù)進(jìn)行 監(jiān)視
      if (key in props) {
        let prop = props[key];
        if (prop === null || !("type" in prop)) {
          prop = { type: prop };
        }
        // prop的observer被watch賦值,也就是小程序組件本身的功能。
        prop.observer = watch[key];
        // 把當(dāng)前的key 放入prop
        props[key] = prop;
      }
    });
    // 經(jīng)過(guò)此方法
    // props: {
    //  a: {
    //    type: String,
    //    observer: (val) {
    //      console.log(val)
    //    }
    //  }
    // }
    options.properties = props;
  }

  // 對(duì)計(jì)算屬性進(jìn)行封裝
  if (computed) {
    options.methods = options.methods || {};
    options.methods.$options = () => vantOptions;

    if (options.properties) {
      
      // 監(jiān)視props,如果props發(fā)生改變,計(jì)算屬性本身也要變
      observeProps(options.properties);
    }
  }
}
observeProps

現(xiàn)在剩下的也就是 observeProps 以及 behavior 兩個(gè)文件了,這兩個(gè)都是為了計(jì)算屬性而生成的,這里我們先解釋 observeProps 代碼

export function observeProps(props) {
  if (!props) {
    return;
  }

  Object.keys(props).forEach(key => {
    let prop = props[key];
    if (prop === null || !("type" in prop)) {
      prop = { type: prop };
    }

    // 保存之前的 observer,也就是上一個(gè)代碼生成的prop
    let { observer } = prop;
    prop.observer = function() {
      if (observer) {
        if (typeof observer === "string") {
          observer = this[observer];
        }

        // 調(diào)用之前保存的 observer
        observer.apply(this, arguments);
      }

      // 在發(fā)生改變的時(shí)候調(diào)用一次 set 來(lái)重置計(jì)算屬性
      this.set();
    };
    // 把修改的props 賦值回去
    props[key] = prop;
  });
}
behavior

最終 behavior,也就算 computed 實(shí)現(xiàn)機(jī)制

// 異步調(diào)用 setData
function setAsync(context: Weapp.Component, data: object) {
  return new Promise(resolve => {
    context.setData(data, resolve);
  });
};

export const behavior = Behavior({
  created() {
    if (!this.$options) {
      return;
    }

    // 緩存
    const cache = {};
    const { computed } = this.$options();
    const keys = Object.keys(computed);

    this.calcComputed = () => {
      // 需要更新的數(shù)據(jù)
      const needUpdate = {};
      keys.forEach(key => {
        const value = computed[key].call(this);
        // 緩存數(shù)據(jù)不等當(dāng)前計(jì)算數(shù)值
        if (cache[key] !== value) {
          cache[key] = needUpdate[key] = value;
        }
      });
      // 返回需要的更新的 computed
      return needUpdate;
    };
  },

  attached() {
    // 在 attached 周期 調(diào)用一次,算出當(dāng)前的computed數(shù)值
    this.set();
  },

  methods: {
    // set data and set computed data
    // set可以使用callback 和 then
    set(data: object, callback: Function) {
      const stack = [];
      // set時(shí)候放入數(shù)據(jù)
      if (data) {
        stack.push(setAsync(this, data));
      }

      if (this.calcComputed) {
        // 有計(jì)算屬性,同樣也放入 stack中,但是每次set都會(huì)調(diào)用一次,props改變也會(huì)調(diào)用
        stack.push(setAsync(this, this.calcComputed()));
      }

      return Promise.all(stack).then(res => {
        // 所有 data以及計(jì)算屬性都完成后調(diào)用callback
        if (callback && typeof callback === "function") {
          callback.call(this);
        }
        return res;
      });
    }
  }
});
寫在后面

js 是一門靈活的語(yǔ)言(手動(dòng)滑稽)

本身 小程序 Component 在 小程序 Page 之后,就要比Page 更加成熟好用,有時(shí)候新的方案往往藏在文檔之中,每次多看幾遍文檔絕不是沒(méi)有意義的。

小程序版本 版本2.6.1 Component 目前已經(jīng)實(shí)現(xiàn)了 observers,可以監(jiān)聽(tīng) props data 數(shù)據(jù)監(jiān)聽(tīng)器,目前 VantComponent沒(méi)有實(shí)現(xiàn),當(dāng)然本身而言,Page 不需要對(duì) prop 進(jìn)行監(jiān)聽(tīng),因?yàn)檫M(jìn)入頁(yè)面壓根不會(huì)變,而data變化本身就無(wú)需監(jiān)聽(tīng),直接調(diào)用函數(shù)即可,所以對(duì)page而言,observers 可有可無(wú)。

該方案也只是對(duì) js 代碼上有vue的風(fēng)格,并沒(méi)在 template 以及 style 做其他文章。

該方案性能一定是有所缺失的,因?yàn)閏omputed是每次set都會(huì)進(jìn)行計(jì)算,而并非根據(jù)set 的 data 來(lái)進(jìn)行操作,在刪減之后我認(rèn)為本身是可以接受。如果本身對(duì)于vue的語(yǔ)法特性需求不高,可以直接利用 Component 來(lái)編寫 Page,選擇不同的解決方案實(shí)質(zhì)上是需要權(quán)衡各種利弊。如果本身是有其他要求或者新的項(xiàng)目,仍舊推薦使用新技術(shù),如果本身是已有項(xiàng)目并且需要維護(hù)的,同時(shí)又想擁有 Vue 特性。可以使用該方案,因?yàn)榇a本身較少,而且本身也可以基于自身需求修改。

同時(shí),vant-weapp是一個(gè)非常不錯(cuò)的項(xiàng)目,推薦各位可以去查看以及star。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/103913.html

相關(guān)文章

  • Vue實(shí)戰(zhàn)—目錄結(jié)構(gòu)可擴(kuò)展項(xiàng)目架構(gòu)設(shè)計(jì)

    摘要:好的項(xiàng)目代碼結(jié)構(gòu)會(huì)大大提升項(xiàng)目的維護(hù)性和可擴(kuò)展性。多說(shuō)無(wú)益,我這里直接給大家一個(gè)示意圖,大家可以按照我給的這個(gè)項(xiàng)目結(jié)構(gòu)組織項(xiàng)目。你連文件目錄都設(shè)計(jì)不好,我拿什么相信你能設(shè)計(jì)出來(lái)可擴(kuò)展的程序 很多人都會(huì)用項(xiàng)目腳手架,也會(huì)跑hello world,然后再寫寫簡(jiǎn)單的todolist。但是再往下深入就難了。比如很多教程和老師都會(huì)說(shuō),大家要多問(wèn)一個(gè)為什么。其實(shí)我想說(shuō)多問(wèn)你妹啊。我都不知道問(wèn)為什么...

    yankeys 評(píng)論0 收藏0
  • 時(shí)至5G時(shí)代,是否還有必要:前端性能優(yōu)化?

    摘要:最近,一個(gè)問(wèn)題總是時(shí)不時(shí)的冒出我的腦海前端性能優(yōu)化時(shí)候還有必要回顧前端性能優(yōu)化然后我找到了雅虎軍規(guī)的條盡量減少請(qǐng)求個(gè)數(shù)須權(quán)衡使用內(nèi)容分發(fā)網(wǎng)絡(luò)為文件頭指定或,使內(nèi)容具有緩存性。那就是時(shí)候停下來(lái),問(wèn)一問(wèn)是否還有必要這樣做。 之前,何同學(xué)的視頻在網(wǎng)上活了一陣子。引發(fā)了我們思考:5G將會(huì)給我們帶來(lái)什么。同時(shí)也回顧了4G乃至3G時(shí)代已經(jīng)給我們帶來(lái)了哪些新的變革。最近,一個(gè)問(wèn)題總是時(shí)不時(shí)的冒出我的...

    Dionysus_go 評(píng)論0 收藏0
  • 自動(dòng)化測(cè)試——我們?cè)诰帉憸y(cè)試時(shí),應(yīng)該注意什么

    摘要:原則具體包括自動(dòng)化獨(dú)立性可重復(fù)簡(jiǎn)單的解釋一下三個(gè)原則單元測(cè)試應(yīng)該是全自動(dòng)執(zhí)行的。為了保證單元測(cè)試穩(wěn)定可靠且便于維護(hù),需要保證其獨(dú)立性。原則編寫單元測(cè)試用例時(shí)為了保證被測(cè)模塊的交付質(zhì)量需要符合原則。與設(shè)計(jì)文檔相結(jié)合來(lái)編寫單元測(cè)試。 本文首發(fā)于泊浮目的專欄:https://segmentfault.com/blog... 背景 最近項(xiàng)目在測(cè)試階段陸陸續(xù)續(xù)的測(cè)出了一些bug.這個(gè)情況剛出現(xiàn)...

    My_Oh_My 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<