欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解vue-class遷移vite的一次踩坑記錄

 更新時(shí)間:2022年02月09日 15:47:23   作者:clench  
本文主要介紹了vue-class遷移vite的一次踩坑記錄,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

what happen

最進(jìn)項(xiàng)目從 vue-cli 遷移到了 vite,因?yàn)槭?vue2 的項(xiàng)目,使用了 vue-class-component 類組件做 ts 支持。
當(dāng)然遷移過程并沒有那么一帆風(fēng)順,瀏覽器控制臺報(bào)了一堆錯(cuò),大致意思是某某方法為 undefined,無法調(diào)用。打印了下當(dāng)前 this,為 undefined 的方法都來自于 vuex-class 裝飾器下的方法。這就是一件很神奇的事,為什么只有 vuex-class 裝飾器下的方法才會為 undefined ?

探究

在網(wǎng)上搜了下并沒有類似的問題,只能自己在 node_modules 中一步一步打斷點(diǎn)看是哪里出了問題。最先覺得有問題的是 vuex-class ,調(diào)試了下 /node_modules/vuex-class/lib/bindings.js 下的代碼,發(fā)現(xiàn) vuex-class 只是做了一層方法替換,通過 createDecorator 方法存到 vue-class-component 下的 __decorators__ 數(shù)組中。

import { createDecorator } from "vue-class-component";

function createBindingHelper(bindTo, mapFn) {
? function makeDecorator(map, namespace) {
? ? // 存入到 vue-class-component 的 __decorators__ 數(shù)組中
? ? return createDecorator(function (componentOptions, key) {
? ? ? if (!componentOptions[bindTo]) {
? ? ? ? componentOptions[bindTo] = {};
? ? ? }
? ? ? var mapObject = ((_a = {}), (_a[key] = map), _a);
? ? ? componentOptions[bindTo][key] =
? ? ? ? namespace !== undefined
? ? ? ? ? ? mapFn(namespace, mapObject)[key]
? ? ? ? ? : mapFn(mapObject)[key];
? ? ? var _a;
? ? });
? }
? function helper(a, b) {
? ? if (typeof b === "string") {
? ? ? var key = b;
? ? ? var proto = a;
? ? ? return makeDecorator(key, undefined)(proto, key);
? ? }
? ? var namespace = extractNamespace(b);
? ? var type = a;
? ? return makeDecorator(type, namespace);
? }
? return helper;
}

那就只能來看看 vue-class-component 了。

vue-class-component 的 @Component 裝飾器會返回一個(gè) vue對象的構(gòu)造函數(shù)。

// vue-class-component/lib/component.js
function Component (options: ComponentOptions<Vue> | VueClass<Vue>): any {
? if (typeof options === 'function') {
? ? return componentFactory(options)
? }
? return function (Component: VueClass<Vue>) {
? ? return componentFactory(Component, options)
? }
}

// 類組件
@Component
export default class HelloWorld extends Vue { ... }

Component 方法會把 class HelloWorld 傳入 componentFactory , 在其內(nèi)部將 name 生命周期 methods computed 等注冊到 options 中,然后傳入 Vue.extend, 返回一個(gè) vue對象的構(gòu)造函數(shù) 。

export function componentFactory(
? Component: VueClass<Vue>,
? options: ComponentOptions<Vue> = {}
): VueClass<Vue> {
? // 。。。無關(guān)代碼

? options.name =
? ? options.name || (Component as any)._componentTag || (Component as any).name;

? const proto = Component.prototype;

? (options.methods || (options.methods = {}))[key] = descriptor.value;

? // typescript decorated data
? (options.mixins || (options.mixins = [])).push({
? ? data(this: Vue) {
? ? ? return { [key]: descriptor.value };
? ? },
? });

? // computed properties
? (options.computed || (options.computed = {}))[key] = {
? ? get: descriptor.get,
? ? set: descriptor.set,
? };

? // add data hook to collect class properties as Vue instance's data
? (options.mixins || (options.mixins = [])).push({
? ? data(this: Vue) {
? ? ? return collectDataFromConstructor(this, Component);
? ? },
? });

? // vuex-class 包裝的方法會在此處注入
? const decorators = (Component as DecoratedClass).__decorators__;
? if (decorators) {
? ? decorators.forEach((fn) => fn(options));
? ? delete (Component as DecoratedClass).__decorators__;
? }

? const Super =
? ? superProto instanceof Vue ? (superProto.constructor as VueClass<Vue>) : Vue;
? const Extended = Super.extend(options);

? // 。。。無關(guān)代碼

? return Extended;
}

至此基本沒有什么問題,那么壓力就來到 vue 這里。返回的 Extended 是 Vue.extend 生成的 vue對象構(gòu)造函數(shù)。

Vue.extend = function (extendOptions) {
? // 。。。無關(guān)代碼

? var Sub = function VueComponent(options) {
? ? this._init(options);
? };

? // 。。。無關(guān)代碼
? return Sub;
};

在 new Extended 的時(shí)候會調(diào)用 _init 初始化 vm 對象。

Vue.prototype._init = function (options) {
? // 。。。無關(guān)代碼

? initLifecycle(vm);
? initEvents(vm);
? initRender(vm);
? callHook(vm, "beforeCreate");
? initInjections(vm); // resolve injections before data/props
? initState(vm);
? initProvide(vm); // resolve provide after data/props
? callHook(vm, "created");

? // 。。。無關(guān)代碼
};

接下來就是無聊的打斷點(diǎn)調(diào)試了,最終找到在執(zhí)行完 initState 方法后 vm 內(nèi)的有些方法變?yōu)榱?undefined ,initState 的作用是將 data methods 等注冊到 vm 上。

function initState(vm) {
? vm._watchers = [];
? var opts = vm.$options;
? if (opts.props) {
? ? initProps(vm, opts.props);
? }
? if (opts.methods) {
? ? initMethods(vm, opts.methods);
? }
? if (opts.data) {
? ? initData(vm);
? } else {
? ? observe((vm._data = {}), true /* asRootData */);
? }
? if (opts.computed) {
? ? initComputed(vm, opts.computed);
? }
? if (opts.watch && opts.watch !== nativeWatch) {
? ? initWatch(vm, opts.watch);
? }
}

再打斷點(diǎn)找到 initData 方法后產(chǎn)生的問題,initData 方法的作用是將 data 對象注冊到 vm 上,如果 data 是一個(gè)函數(shù),則會調(diào)用該函數(shù),那么問題就出現(xiàn)在 getData 中的 data.call(vm, vm) 這一句了。

function initData(vm) {
? var data = vm.$options.data;
? data = vm._data = typeof data === "function" ? getData(data, vm) : data || {};

? // 。。。無關(guān)代碼
}

function getData(data, vm) {
? // #7573 disable dep collection when invoking data getters
? pushTarget();

? try {
? ? const a = data.call(vm, vm);
? ? return a;
? } catch (e) {
? ? handleError(e, vm, "data()");
? ? return {};
? } finally {
? ? popTarget();
? }
}

調(diào)用的 data.call(vm, vm) 是 vue-class-component 注冊的方法。好吧,又回到了 vue-class-component,我們來看看 vue-class-component 的代碼。

export function componentFactory(
? Component: VueClass<Vue>,
? options: ComponentOptions<Vue> = {}
): VueClass<Vue> {
? // 。。。無關(guān)代碼
? (options.mixins || (options.mixins = [])).push({
? ? data(this: Vue) {
? ? ? return collectDataFromConstructor(this, Component);
? ? },
? });
? // 。。。無關(guān)代碼
}

在上面的 componentFactory 方法中,data 返回一個(gè) collectDataFromConstructor 方法。在 collectDataFromConstructor 我們應(yīng)該就可以解開謎題了。

function collectDataFromConstructor(vm, Component) {
? Component.prototype._init = function () {
? ? var _this = this;
? ? // proxy to actual vm
? ? var keys = Object.getOwnPropertyNames(vm); // 2.2.0 compat (props are no longer exposed as self properties)

? ? if (vm.$options.props) {
? ? ? for (var key in vm.$options.props) {
? ? ? ? if (!vm.hasOwnProperty(key)) {
? ? ? ? ? keys.push(key);
? ? ? ? }
? ? ? }
? ? }

? ? keys.forEach(function (key) {
? ? ? Object.defineProperty(_this, key, {
? ? ? ? get: function get() {
? ? ? ? ? return vm[key];
? ? ? ? },
? ? ? ? set: function set(value) {
? ? ? ? ? vm[key] = value;
? ? ? ? },
? ? ? ? configurable: true,
? ? ? });
? ? });
? }; // should be acquired class property values

? var data = new Component(); // restore original _init to avoid memory leak (#209)

? // 。。。無關(guān)代碼

? return data;
}
function Vue(options) {
? this._init(options);
}

傳下來的 Component 參數(shù)即 export default class HelloWorld extends Vue { ... }, new Component() 會獲取到 HelloWorld 內(nèi)的所有參數(shù)。 Component 繼承于 Vue ,因此在 new Component() 時(shí),會像 Vue 一樣先調(diào)用一遍 _init 方法,collectDataFromConstructor 置換了 Component 的 _init。

在置換的 _init 方法中,會遍歷 vm 上的所有屬性,并且將這些屬性通過 Object.defineProperty 再指回 vm 上。原因在于 initData 前會先 initProps initMethods 意味著,那么在 new Component() 時(shí),探測到屬于 props methods 的值時(shí)就會指向 vm,而剩下的就是 data 值。

整個(gè)流程跑下來好像沒什么問題。不過既然使用了 Object.defineProperty 做 get set ,那會不會和 set 方法有關(guān)系呢?在 set 方法里打了一層斷點(diǎn),果然觸發(fā)了,觸發(fā)的條件有些奇特。

@Component
export default class HelloWorld extends Vue {
? // vuex
? @model.State
? count: number;
? @model.Mutation("increment")
? increment: () => void;
? @model.Mutation("setCount")
? setCount: () => void = () => {
? ? this.count = this.count + 1;
? };

? // data
? msg: string = "Hello Vue 3 + TypeScript + Vite";
? // ? methods
? incrementEvent() {
? ? console.log(this);
? ? this.increment();
? ? this.msg = this.msg + " + " + this.count;
? }
? // ? 生命周期
? beforeCreate() {}
? created() {
? ? console.log(this);
? ? this.msg = this.msg + " + " + this.count;
? }
}

上面是一個(gè)很基礎(chǔ)的類組件,increment setCount 的 set 觸發(fā),一個(gè)被傳入了 undefined 一個(gè)被傳入 () => { this.count = this.count + 1 },兩個(gè)都屬于 methods 但都是不是以 fn(){} 的方式賦予初始值,所以 incrementEvent 的 set 沒有觸發(fā),increment 被傳入了 undefined,setCount 被傳入了一個(gè)函數(shù)

class A {
? increment;
? setCount = () => {};
? incrementEvent() {}
}

increment 和 setCount 為一個(gè)變量,而 incrementEvent 會被看做一個(gè)方法

奇怪的是在 vue-cli 中沒什么問題,set 方法不會觸發(fā),為什么切換到 vite 之后 會觸發(fā) set 重置掉一些變量的初始值。我想到是不是二者的編譯又問題。我對比了下二者編譯后的文件,果然。

vue-cli

export default class HelloWorld {
? constructor() {
? ? this.setCount = () => {
? ? ? this.count = this.count + 1;
? ? };
? ? // data
? ? this.msg = "Hello Vue 3 + TypeScript + Vite";
? }
? // ? methods
? incrementEvent() {
? ? console.log(this);
? ? this.increment();
? ? this.msg = this.msg + " + " + this.count;
? }
? // ? 生命周期
? beforeCreate() {}
? created() {
? ? console.log(this);
? ? this.msg = this.msg + " + " + this.count;
? }
}

vite

export default class HelloWorld {
? // vuex
? count;
? increment;
? setCount = () => {
? ? this.count = this.count + 1;
? };
? // data
? msg = "Hello Vue 3 + TypeScript + Vite";
? // ? methods
? incrementEvent() {
? ? console.log(this);
? ? this.increment();
? ? this.msg = this.msg + " + " + this.count;
? }
? // ? 生命周期
? beforeCreate() {}
? created() {
? ? console.log(this);
? ? this.msg = this.msg + " + " + this.count;
? }
}

可以看到 vue-cli vite 的編譯結(jié)果并不一致,vite 比 vue-cli 多出了 count increment 兩個(gè)默認(rèn)值,這兩個(gè)值默認(rèn)值是 undefined,在 vue-cli 并沒有編譯進(jìn)去。下面只能去翻 vite 文檔了,一個(gè)屬性吸引了我。

查了下這個(gè) useDefineForClassFields 屬性,簡單來講,useDefineForClassFields 為 false 的情況下 ts 會 跳過為 undefined 的變量,為 true 就會將默認(rèn)值為 undefined 的變量屬性依然編譯進(jìn)去。正常情況下不會有什么問題,但是 vue-class-component 會對 props methods 的屬性做一層劫持,那 new 初始化 的時(shí)候探測到這些值就會觸發(fā) set,如果沒有默認(rèn)值就會被賦值為 undefined。

解決

想要解決很簡單,只要在 tsconfig 中加入 useDefineForClassFields 屬性,并設(shè)置為 false 就可以了。

{
? "compilerOptions": {
? ? "target": "ESNext",
? ? "useDefineForClassFields": false,
? ? "module": "ESNext",
? ? "lib": ["ESNext", "DOM"],
? ? "moduleResolution": "Node",
? ? "strict": true,
? ? "sourceMap": false,
? ? "resolveJsonModule": true,
? ? "esModuleInterop": true,
? ? "noEmit": true,
? ? "noUnusedLocals": true,
? ? "noUnusedParameters": true,
? ? "noImplicitReturns": true
? },
? "include": ["./src"]
}

總結(jié)

在轉(zhuǎn)到 vite 的過程中,還是有許多坑要踩的,有時(shí)候并不是 vite 的問題,而是來自多方的問題,useDefineForClassFields 帶來的變化也不僅僅是會編譯為 undefined 的屬性,可以多了解一下,也可以拓寬一些知識。

到此這篇關(guān)于詳解vue-class遷移vite的一次踩坑記錄的文章就介紹到這了,更多相關(guān)vue-class遷移vite內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue實(shí)現(xiàn)向PDF文件中添加二維碼

    Vue實(shí)現(xiàn)向PDF文件中添加二維碼

    這篇文章主要為大家詳細(xì)介紹了如何利用Vue實(shí)現(xiàn)向PDF文件中添加二維碼的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2023-06-06
  • Vue 2.0學(xué)習(xí)筆記之Vue中的computed屬性

    Vue 2.0學(xué)習(xí)筆記之Vue中的computed屬性

    本篇文章主要介紹了Vue 2.0學(xué)習(xí)筆記之Vue中的computed屬性,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • Vue中的組件詳談

    Vue中的組件詳談

    這篇文章主要介紹了Vue的組件,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-10-10
  • 詳解從新建vue項(xiàng)目到引入組件Element的方法

    詳解從新建vue項(xiàng)目到引入組件Element的方法

    本篇文章主要介紹了詳解從新建vue項(xiàng)目到引入組件Element的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • Vue中用props給data賦初始值遇到的問題解決

    Vue中用props給data賦初始值遇到的問題解決

    這篇文章主要介紹了Vue中用props給data賦初始值遇到的問題解決,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-11-11
  • vue+antd實(shí)現(xiàn)折疊與展開組件

    vue+antd實(shí)現(xiàn)折疊與展開組件

    這篇文章主要為大家詳細(xì)介紹了vue+antd實(shí)現(xiàn)折疊與展開組件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • vue關(guān)于自定義指令與v-if沖突的問題

    vue關(guān)于自定義指令與v-if沖突的問題

    這篇文章主要介紹了vue關(guān)于自定義指令與v-if沖突的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue實(shí)現(xiàn)同時(shí)設(shè)置多個(gè)倒計(jì)時(shí)

    vue實(shí)現(xiàn)同時(shí)設(shè)置多個(gè)倒計(jì)時(shí)

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)同時(shí)設(shè)置多個(gè)倒計(jì)時(shí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • Vue 3.x+axios跨域方案的踩坑指南

    Vue 3.x+axios跨域方案的踩坑指南

    這篇文章主要給大家介紹了關(guān)于Vue 3.x+axios跨域方案的踩坑指南,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Vue 3.x具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • web前端Vue報(bào)錯(cuò):Uncaught?(in?promise)?TypeError:Cannot?read?properties?of?nu解決

    web前端Vue報(bào)錯(cuò):Uncaught?(in?promise)?TypeError:Cannot?read?

    這篇文章主要給大家介紹了關(guān)于web前端Vue報(bào)錯(cuò):Uncaught?(in?promise)?TypeError:Cannot?read?properties?of?nu的解決方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01

最新評論