Vue3

Vue3.0的優(yōu)勢(shì)

  • 性能比Vue2.x快1.2~2倍
  • 按需編譯军俊,體積比Vue2.x更小
  • 組合API(類似React Hooks)
  • 更好的TS支持
  • 暴露自定義渲染API

Vue3.0如何變快得微谓?

  • diff方法優(yōu)化
    • Vue2中得虛擬dom是進(jìn)行全量得對(duì)比
    • Vue3新增了靜態(tài)標(biāo)記
      • 在與上次虛擬節(jié)點(diǎn)進(jìn)行對(duì)比時(shí)候,只對(duì)比帶有patch flag得節(jié)點(diǎn)去扣,并且可以通過flag(例如:1就代表動(dòng)態(tài)文本節(jié)點(diǎn))得信息得知當(dāng)前節(jié)點(diǎn)要對(duì)比得具體內(nèi)容
  • hoistStatic 靜態(tài)提升
    • Vue2中無論元素是否參與更新蔚携,每次都會(huì)重新創(chuàng)建
    • Vue3中對(duì)于不參與更新得元素,只會(huì)被創(chuàng)建一次银亲,之后會(huì)在每次渲染得時(shí)候被不停得復(fù)用
  • cacheHandlers 事件偵聽器緩存
    • 默認(rèn)情況下onClick會(huì)被視為動(dòng)態(tài)綁定艺骂,所以每次都會(huì)去追蹤它得變化汞舱,但是因?yàn)槭峭粋€(gè)函數(shù)伍纫,所以沒有追蹤變化(也是通過flag),直接緩存起來復(fù)用即可
  • SSR渲染
    • 當(dāng)有大量靜態(tài)得內(nèi)容得時(shí)候昂芜,這些內(nèi)容會(huì)被當(dāng)做純字符串推進(jìn)一個(gè)buffer里面莹规,即使存在動(dòng)態(tài)得綁定,會(huì)通過模板插值嵌入進(jìn)去泌神,這樣會(huì)比通過虛擬dom
      渲染得快上很多很多
    • 靜態(tài)內(nèi)容大到一定量級(jí)時(shí)候良漱,會(huì)用_createStaticVNode方法在客戶端去生成一個(gè)static node,這些靜態(tài)node,會(huì)被直接innerHTML,就不需要?jiǎng)?chuàng)建對(duì)象欢际,然后根據(jù)對(duì)象渲染了

Vue3.0快速上手

當(dāng)然也有腳手架和webpack的方式母市,但是此時(shí)只使用這個(gè) Vite打包使用的是rollup

  • 什么是Vite?

    Vite是Vue作者開發(fā)的一款意圖取代webpack的工具,其實(shí)現(xiàn)原理是利用ES6的import會(huì)發(fā)送請(qǐng)求去加載文件的特性损趋,攔截這些請(qǐng)求患久,做一些預(yù)編譯,省去webpack冗長的打包時(shí)間

  • 安裝Vite

    npm i -g create-vite-app

  • 創(chuàng)建項(xiàng)目

    create-vite-app projectName

  • 安裝依賴然后運(yùn)行

腳手架形式

  • npm install -g @vue/cli
  • vue create hello-vue3

知識(shí)點(diǎn)補(bǔ)充

<template>
  <div>
    <!-- 注意此處,本來bind里面綁定的應(yīng)該是變量蒋失,但是此時(shí)傳遞固定值則需要加'' -->
    <a v-bind:[myHref]="'https://www.baidu.com'">跳轉(zhuǎn)百度</a>
    <!-- 對(duì)象也可遍歷 -->
    <li v-for="(value, name, index) in myObject" :key="index">
      {{ name }}--{{ value }}---{{ index }}
    </li>
    <button @click="one($event), two($event)">多事件處理</button>
  </div>
</template>
<script>
export default {
  name: "App",
  data() {
    return {
      infos: "嘿嘿",
      infos1: "哈哈",
      myHref: "href",
      // myHref:'title',
      myObject: {
        title: "ahdhasa",
        name: "zq",
      },
    };
  },
  methods: {
    one(e) {
      console.log(e);
    },
    two(e) {
      console.log(e);
    },
  },
};
</script>

自定義事件

<template>
  <div>
    <!-- 自定義事件 -->
    <Child @send="getChild" />
  </div>
</template>
<script>
import Child from "./components/Child";
export default {
  name: "App",
  components: {
    Child
  },
  methods: {
    getChild(data) {
      console.log("子組件觸發(fā)了該方法", data);
    }
  },
};
</script>
<template>
  <div>
      <p>Child</p>
      <button @click="test">執(zhí)行父組件的自定義事件</button>
  </div>
</template>
<script>
export default {
    //自定義事件還可以添加上校驗(yàn)返帕,但是一般不需要
    emits:{
        //這個(gè)send需要和自定義事件名稱保持一致
        send:({msg})=>{
            //注意:雖然驗(yàn)證,但是還是會(huì)把數(shù)據(jù)傳遞到父組件的
            if (msg.length>10) {
                return true
            } else {
                console.log(111,"數(shù)據(jù)長度不足");
                return false
            }
        }
    },
    //建議定義的所有發(fā)出的事件都記錄如下篙挽,雖然不記錄也可以
    // emits:["xxx-xxx"],
    data(){
        return{
            msg:'子組件數(shù)據(jù)'
        }
    },
    methods:{
        test(){
            //send就是組件上面的自定義函數(shù)名
            // this.$emit('send','子組件數(shù)據(jù)')
           this.$emit('send',{msg:this.msg})
        }
    }
}
</script>

自定義輸入框?qū)崿F(xiàn)雙向數(shù)據(jù)綁定

  • 默認(rèn)情況下荆萤,組件上的v-model使用modelvalue作為prop和update:modelValue作為事件。
  • 可以通過v-model傳遞參數(shù)來修改這些名稱
<template>
  <div>
    <!-- 自定義輸入框組件铣卡,實(shí)現(xiàn)雙向數(shù)據(jù)綁定,infos是自定義屬性名稱链韭,不定的 -->
    <MyComponent v-model:infos="infos" v-model:infos1="infos1" />
  </div>
</template>
<script>
import MyComponent from "./components/MyComponent";
export default {
  name: "App",
  components: {
    MyComponent
  },
  data() {
    return {
      infos: "嘿嘿",
      infos1: "哈哈",
    };
  }
};
</script>
<template>
  <!-- update是固定的,infos的app.vue中的關(guān)鍵字是自定義的煮落,對(duì)應(yīng)起來就行 -->
  <input
    type="text"
    :value="infos"
    @input="$emit('update:infos', $event.target.value)"
  />
  <input
    type="text"
    :value="infos1"
    @input="$emit('update:infos1', $event.target.value)"
  />
</template>

<script>
export default {
  props: ["infos","infos1"],
};
</script>

非prop的attribute繼承

  • 一個(gè)非prop的attribute是指?jìng)飨蛞粋€(gè)組件敞峭,但是該組件并沒有相應(yīng)的props或emits定義的attribute。
  • 常見的示例包含class style id屬性
<template>
  <div>
    <!-- 非prop的attribute繼承 -->
    <Date class="datecss" />
  </div>
</template>
  • Date.vue
<template>
<!-- 其實(shí)可以發(fā)現(xiàn)州邢,此處div被添加了datecss的樣式 -->
  <div>
      hello world
  </div>
</template>

自定義attribute繼承

  • 如果你不希望組件的根元素繼承attribute儡陨,可以在組件的選項(xiàng)中設(shè)置inheritAttrs:false;
  • 例如:禁用attribute繼承的常見情況是需要將attribute應(yīng)用于根節(jié)點(diǎn)之外的其他元素
  • 通過設(shè)置inheritAttrs:false,可以訪問組件的$attr的property量淌,該property包括組件props和emits property中未包含的所有屬性(例如:class style v-on監(jiān)聽器等
<template>
  <div>
    <!-- 自定義attribute繼承 -->
    <DatePicker data-time="2020-11-11" />
  </div>
</template>
  • DatePicker.vue
<template>
<!-- 而且vue3允許多個(gè)根節(jié)點(diǎn),即使默認(rèn)繼承如果出現(xiàn)多個(gè)根節(jié)點(diǎn)也會(huì)報(bào)警告嫌褪,所以多個(gè)根節(jié)點(diǎn)也需要指定哪個(gè)繼承,例如 v-bind="$attrs" -->
  <!-- 本來data數(shù)據(jù)屬于attribute呀枢,默認(rèn)是給了當(dāng)面的最外層div,可以通過查看dom確定 -->
  <div class="datapicker">
      <!-- 但是此時(shí)需要讓input使用笼痛,而不是上層的div -->
      <!-- 通過v-bind="$attrs"實(shí)現(xiàn)裙秋,前提是inhertAttrs:false,//禁用默認(rèn)繼承 -->
    <input type="date" v-bind="$attrs" />
  </div>
</template>

<script>
export default {
    inhertAttrs:false,//禁用默認(rèn)繼承
};
</script>

自定義dialog內(nèi)部關(guān)閉dialog

<template>
  <div>
    <button @click="isVisible=true">彈出彈窗</button>
    <Dialog :visible="isVisible" @close-modal="isVisible=false"/>
    <Home3/>
  </div>
</template>
<script>
import Dialog from "./components/Dialog";
export default {
  name: "App",
  components: {
    Dialog
  },
  data() {
    return {
      isVisible:false
    };
  }
};
</script>
  • Dialog.vue
<template>
  <teleport to="body">
    <div v-if="visible">
      我是彈窗,樣式不計(jì)缨伊,只說明功能
      <button @click="$emit('close-modal')">關(guān)閉</button>
    </div>
  </teleport>
</template>

<script>
export default {
  props: ["visible"],
};
</script>

teleport

  • Vue3中的組件模板屬于該組件摘刑,有時(shí)候想把模板的內(nèi)容移動(dòng)到當(dāng)前組件之外的dom中,這個(gè)時(shí)候就可以使用Teleport了
  • 表示把teleport內(nèi)包含的內(nèi)容顯示到body中
<teleport to="body">
內(nèi)容
</teleport>
同理可以 to="#app"這種形式

例如:彈窗組件刻坊,點(diǎn)擊彈出枷恕,內(nèi)部一定通過css樣式確定該組件的位置,但是彈窗組件所有位置谭胚,可能外部有定位樣式徐块,影響了本身彈窗的樣式,
導(dǎo)致顯示混亂灾而,這時(shí)候就可以通過teleport然后指定顯示插入的節(jié)點(diǎn)位置胡控,

例如放到body節(jié)點(diǎn),則基本上無影響了旁趟。雖然代碼形式上可能彈窗再某個(gè)組件內(nèi)
部昼激,但是實(shí)際顯示時(shí)候卻是再body根節(jié)點(diǎn)上面

vue3生命周期

  • beforeCreate:實(shí)例剛被創(chuàng)建
  • created:實(shí)例已經(jīng)創(chuàng)建完成
  • beforeMount:模板編譯之前
  • mounted:模板編譯完成
  • beforeUpdate:數(shù)據(jù)更新之前
  • updated:數(shù)據(jù)更新完畢
  • actived: keep-alive緩存的組件激活時(shí)調(diào)用
  • deactived: keep-alive緩存的組件停用時(shí)調(diào)用
  • beforeUnmount:對(duì)應(yīng)2.x的beforeDestory,頁面銷毀的時(shí)候要保存一些數(shù)據(jù),就在此處
  • unmounted:2.x的destoryed 實(shí)例銷毀完成
a.png
import {onMounted,oUnmounted} from 'vue';
set(){
    onMounted(()=>{
        console.log('onMounted');
    });
}

Compositon API介紹

reactive

作用:創(chuàng)建響應(yīng)式對(duì)象橙困,非包裝對(duì)象敛劝,可以認(rèn)識(shí)是模板中的狀態(tài)

  • template可以放兄弟節(jié)點(diǎn)
  • reactive類似useState,如果參數(shù)是字符串纷宇,數(shù)字夸盟,會(huì)報(bào)警告,value cannot be made reactive,所以應(yīng)該設(shè)置對(duì)象像捶,這樣可以數(shù)據(jù)驅(qū)動(dòng)頁面
<div>
    {{countobj.count}}-<button @click="add()">Add</button>
</div>
setup(){
    const countobj =reactive({count:0});
    const add=() => {
        countobj.count++;
    }
    return {countobj,add}
}   
<template>
  <div>child-hooks-{{ mytitle }}-{{ mytext }}</div>
  <div>
    navbar-<button @click="handleClick">navbar-click</button>
  </div>
</template>
<script>
import { ref } from "vue";
export default {
  props: ["mytitle"],
  //setup內(nèi)部沒有this上陕,可以這樣獲取prosp;
  setup(props, { emit }) {
    // console.log(props.mytitle)
    const mytext = ref(props.mytitle + "11111111111111111");
     const handleClick = () => {
      emit('kerwinevent')
    }
    return {
      mytext,
      handleClick
    };
  },
};
</script>
  • 下面是父級(jí)接受emit
<template>
  <div>
    <navbar @kerwinevent="handleChange"/>
    <sidebar v-if="state.isShow"/>
  </div>
</template>

ref

作用:創(chuàng)建一個(gè)包裝式對(duì)象,含有一個(gè)響應(yīng)式屬性value拓春。它和reactive的差別释簿,就說前者沒有包裝屬性value

const count =ref(0); 可以接收普通數(shù)據(jù)類型,count.value++

<div>
    {{count}}-<button @click="add()">Add</button>
</div>
setup(){
    const count =ref(0); 
    const add=() => {
        count.count++;
    }
    return {count,add}
}  

ref嵌套在reactive中

<template>
    <div class="home">
        home-{{count}}--{{state.count}}
        <button @click="add">click</button>
    </div>
</template>
<script>
import {reactive,ref} from 'vue';
export default{
    name:'Home',
    setup(){
        const count = ref(0);
        const state = reactive({count})
        const add=() => {
            state.count++
            //state.count跟ref  count都會(huì)更新硼莽;
        }
        return{ count,add,state}
    }
}
</script>

toRefs

默認(rèn)直接展開state庶溶,那么此時(shí)reactive數(shù)據(jù)變成普通數(shù)據(jù),通過toRefs,可以把reactive里的每個(gè)屬性懂鸵,轉(zhuǎn)化為ref對(duì)象偏螺,這樣展開后,就會(huì)變成多個(gè)ref對(duì)象匆光,以然具有響應(yīng)式特性

<template>
    <div class="home">
        home-{{count}}
        <button @click="add">click</button>
    </div>
</template>
<script>
import {reactive,ref} from 'vue';
export default{
    name:'Home',
    setup(){
        const state = reactive({count})
        const add=() => {
            state.count++
        }
        return{ add,...toRefs(state)}
    }
}
</script>

ref訪問dom或者組件

<input type="text" ref="myinput">
const myinput =ref(null);
console.log(myinput.value.value);

計(jì)算屬性

set(){
    const mytext = ref('');
    const computedSum=computed(()=>mytext.value.substring(0,1).toUpperCase()+mytext.value.substring(1)+mytext.value.substring(1));
    return {mytext,computedSum}
}

watch

監(jiān)聽器watch是一個(gè)方法套像,它包含兩個(gè)參數(shù)

const reactivedata=reactive({count:1});
const text=ref("");
watch(()=>reactivedata.count,val=>{

});
watch(text,val=>{

})
//注意:reactive和ref的監(jiān)聽有細(xì)微區(qū)別

第一個(gè)參數(shù)是監(jiān)聽的值,count.value表示當(dāng)count.value發(fā)生變化就會(huì)觸發(fā)監(jiān)聽器的回調(diào)函數(shù)终息,即第二個(gè)參數(shù)夺巩,底二個(gè)參數(shù)可以執(zhí)行監(jiān)聽時(shí)候的回調(diào)

vue3組合api的使用

通俗來說,option API在中小型項(xiàng)目沒問題周崭,但是大型項(xiàng)目柳譬,建議使用composition API,方便邏輯復(fù)用

<template>
  <div>
    <!-- vue3組合api的使用 -->
    <Home3/>
  </div>
</template>
  • Home3.vue
<template>
  <div>------{{ title }}----------</div>
  <button @click="getTitle">getTitle</button>
  <button @click="getInfo">getInfo</button>
  <button @click="setTitle">setTitle</button>
  <p>我是分割線----------------</p>
  {{ descrip }}--{{ auth }}

  <p>{{ fullName }}</p>
  <p>watchEffect功能演示</p>
  <p>{{ data.num }}</p>
</template>

<script>
import { ref, reactive, toRefs, computed, watchEffect } from "vue";
export default {
  setup() {
    //定義響應(yīng)式數(shù)據(jù)-如果不用這些定義,則只是普通數(shù)據(jù)续镇,非響應(yīng)式的(原始對(duì)象)
    //ref:基本數(shù)據(jù)類型  reactive:對(duì)象
    let title = ref("我是一個(gè)標(biāo)題");
    let info = reactive({ info: "infossss" });
    let article = reactive({ descrip: "描述", auth: "作者" });
    // title=readonly(title);  //只讀
    let getTitle = () => {
      console.log(title.value); //注意此處美澳,是title.value
    };
    let setTitle = () => {
      title.value = "修改的";
    };
    let getInfo = () => {
      console.log(info.info);
    };

    const user = reactive({
      fn: "1",
      ln: "2",
    });
    //計(jì)算屬性
    const fullName = computed(() => {
      return user.fn + "------" + user.ln;
    });

    //watchEffect功能
    let data = reactive({
      num: 1,
    });
    watchEffect(() => {
      //和watch不同,不論改不改變監(jiān)聽得值磨取,都會(huì)執(zhí)行該函數(shù)人柿,即最開始時(shí)候也會(huì)執(zhí)行一次的,也算作更新
      //而且watch必須監(jiān)聽data即大對(duì)象才能監(jiān)聽忙厌,但是watchEffect可以監(jiān)聽具體的data.num,
      //也就是說watchEffect可以監(jiān)聽當(dāng)前回調(diào)里面所有數(shù)據(jù)變化凫岖,即使只是打印一個(gè)log
      console.log(`num=${data.num}`);
    });

    // watch(num1,(newValue,oldValue)=>{

    // })


    setInterval(() => {
      data.num++;
    }, 1000);

    return {
      title,
      info,
      getTitle,
      getInfo,
      setTitle,
      data,
      // ...article  //三點(diǎn)運(yùn)算符相當(dāng)于把a(bǔ)rticle內(nèi)部對(duì)象解構(gòu)到這里,但是不是響應(yīng)式數(shù)據(jù),解決方案toRefs
      ...toRefs(article),
      fullName,
    };
  },
};
</script>
  • watchEffect
    在響應(yīng)式的跟蹤其依賴項(xiàng)時(shí)立即運(yùn)行一個(gè)函數(shù)逢净,并在更改依賴項(xiàng)時(shí)重新運(yùn)行它.
  • watch和watchEffect的區(qū)別
    • 懶執(zhí)行哥放,也就是說僅在偵聽的源變更時(shí)才執(zhí)行回調(diào)
    • 更明確哪些狀態(tài)的改變會(huì)觸發(fā)偵聽器重新運(yùn)行
    • 訪問偵聽狀態(tài)變化前后的值

Provider Inject

通常需要將數(shù)據(jù)從父組件傳遞到子組件時(shí)歼指,使用props。但是甥雕,有一些深嵌套的組件踩身,需要來自深嵌套子組件中父組件的某些內(nèi)容;這種情況社露,相當(dāng)不方便

可以使用provide和inject對(duì)父組件可以作為其子組件的依賴項(xiàng)提供程序挟阻,而不管組件層次結(jié)構(gòu)有多深。
這個(gè)特性有兩部分峭弟,父組件provide選項(xiàng)來提供數(shù)據(jù)附鸽,子組件有一個(gè)inject選項(xiàng)來開始使用這個(gè)數(shù)據(jù)

事件總線

  • vue3.x從實(shí)例移除了onoff $once
  • 但是$emit仍然是現(xiàn)有api的一部分,可是只能實(shí)現(xiàn)子組件觸發(fā)父組件的方法
  • 實(shí)現(xiàn)非父子組件通信瞒瘸,可以使用第三方
npm install --save mitt
  • 新建model\event.js
import mitt from 'mitt'
const VueEvent=mitt();
export default VueEvent;

混入

  • 混入和當(dāng)前都有同一個(gè)屬性坷备,則會(huì)應(yīng)用當(dāng)前組件的屬性
  • 也可以全局配置mixin,這樣就不需要再每個(gè)組件里面配置了

hooks對(duì)應(yīng)的生命周期

1.png

setup函數(shù)說明

setup函數(shù)情臭,是在beforecreate鉤子之前完成的省撑,所以data中數(shù)據(jù)和methods中函數(shù)是無法使用的,setup中的this被vue修改成了underfined俯在;
并且setup函數(shù)必須是同步的竟秫,不能是異步的(async)

  • beforeCreate:組件剛剛被創(chuàng)建出來,組件的data和methods還沒有初始化好
  • created: 組件剛剛被創(chuàng)建出來朝巫,并且組件的data和methods已經(jīng)初始化好了
  1. 什么是reactive鸿摇?
  • reactive是Vue3中提供的實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的方法
  • 而在Vue3中響應(yīng)式數(shù)據(jù)是通過ES6的Proxy實(shí)現(xiàn)的
  1. 什么是ref?
  • ref和reactive一樣,也是用來實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的方法
  • 由于reactive必須傳遞一個(gè)對(duì)象劈猿,所以導(dǎo)致在開發(fā)中如果只想讓某個(gè)變量實(shí)現(xiàn)響應(yīng)式的時(shí)候會(huì)非常麻煩,所以提供了ref
  • ref本質(zhì)還是reactive潮孽,ref(xx)=>reactive({value:xx})
  • ref注意點(diǎn):Vue中使用ref的值不用通過value獲取揪荣,但是js中使用的話必須使用value獲取
  • Vue是如果判斷數(shù)據(jù)是ref還是reactive的?通過當(dāng)前數(shù)據(jù)的__v_ref來判斷的往史,如果是true就代表是ref數(shù)據(jù)仗颈,reactvie沒有這個(gè)私有屬性
  • 可以通過isRef和isReactive來判斷數(shù)據(jù)類型
<template>
  <div>
    <p>{{ count }}</p>
    <button @click="fn">按鈕</button>
    <li v-for="stu in state.stus" :key="stu.id">{{ stu.name }}-{{ stu.id }}</li>
  </div>
</template>

<script>
import { reactive, ref } from "vue";

export default {
  name: "App",
  //setup函數(shù)是組合API得入口函數(shù)
  setup() {
    //通過這個(gè)可知,組合API內(nèi)部邏輯可以是其他函數(shù)椎例,甚至其他js文件導(dǎo)出的邏輯
    let { fn, count } = ctrlBtn();
    //監(jiān)聽復(fù)雜對(duì)象,reactive的參數(shù)必須是對(duì)象或者數(shù)組
    let state = reactive({
      stus: [
        {
          id: 1,
          name: "zq",
        },
      ],
    });
    return {
      state,
      fn,
      count,
    };
  },
};
function ctrlBtn() {
  //定義名稱叫做count得變量挨决,這個(gè)變量得初始值是0
  //ref函數(shù)只能監(jiān)聽簡(jiǎn)單類型的變化,不能監(jiān)聽復(fù)雜類型變化订歪,例如數(shù)組脖祈,對(duì)象
  let count = ref(0);
  function fn() {
    count.value += 1;
  }
  return { count, fn };
}
</script>

監(jiān)聽

  • 默認(rèn)情況下,無論是通過ref還是reactive都是遞歸監(jiān)聽
  • 遞歸監(jiān)聽數(shù)據(jù)量比較大刷晋,消耗性能
  • 非遞歸監(jiān)聽(shallowReactive,shallowRef),shallowReactive非遞歸監(jiān)聽是只監(jiān)聽第一層變化盖高,只生成第一層的proxy對(duì)象(可以打印說明)慎陵;但是如果修改第一層數(shù)據(jù),同時(shí)內(nèi)部的數(shù)據(jù)也被修改的話喻奥,可以發(fā)現(xiàn)UI界面都變化了席纽,即使沒被監(jiān)聽的也變化了,這是因?yàn)榈谝粚有薷牧擞|發(fā)了render撞蚕,如果不修改第一層數(shù)據(jù)润梯,發(fā)現(xiàn)UI無變化。當(dāng)然不論是遞歸還是不遞歸監(jiān)聽甥厦,數(shù)據(jù)都被變化纺铭,只是界面UI更新與否的差別
  • 注意點(diǎn):如果是通過shallowRef創(chuàng)建數(shù)據(jù),那么vue監(jiān)聽的是.value的變化矫渔,并不是第一層數(shù)據(jù)的變化彤蔽,即例如let state=shallowRef();其實(shí)是state被包裝成了proxy。
  • triggerRef:傳入的參數(shù)會(huì)更新UI,一般用于庙洼,有的是非遞歸監(jiān)聽顿痪,但是我就想更新某個(gè)數(shù)據(jù)時(shí)候使用
  • 本質(zhì):shallowRef(xx)=>shallowRef({value:xx}),監(jiān)聽的是.value的變化油够,因?yàn)関alue其實(shí)才是第一層
let state=shallowRef({
    a:'a',
    b:{
        c:'c'
    }
});
//a變化是不會(huì)更新界面的
//如下才行
state.value={....}

//triggerRef:主動(dòng)更新蚁袭,但是沒有triggerReactive,如果是reactive類型數(shù)據(jù)是無法主動(dòng)觸發(fā)UI更新的
triggerRef(state);

toRaw

  • 相當(dāng)于reactive的逆向石咬,獲取的是傳入的參數(shù)
  • 應(yīng)用場(chǎng)景是揩悄,不需要更新UI,但是想操作數(shù)據(jù)的時(shí)候
  • 注意:如果是ref的數(shù)據(jù)鬼悠,則需要.value形式才能拿到原始數(shù)據(jù)
let state = reactive({
     name:'zq'
    });
//state其實(shí)是把傳入的對(duì)象包裝成了一個(gè)proxy

//獲取類型的原始數(shù)據(jù)
toRaw(state);//其實(shí)就是reactive傳入的對(duì)象

//ref也是可以傳遞對(duì)象的
let obj={name:'zq'}
let ha=ref(obj);
toRaw(ha.value);

markRaw

  • 作用是避免響應(yīng)式删性,UI不會(huì)再更新了
let obj={name:'zq'}
obj=markRaw(obj);
let state=reactive(obj);
//后面再怎么更新,UI也不會(huì)有變化

toRef

把一個(gè)響應(yīng)式對(duì)象轉(zhuǎn)換成普通對(duì)象焕窝,該普通對(duì)象的每個(gè)property都是一個(gè)ref蹬挺,和響應(yīng)式對(duì)象property--對(duì)應(yīng)

  • 效果和ref類似,但是更多的應(yīng)用場(chǎng)景是讓局部數(shù)據(jù)變成響應(yīng)式
  • 不會(huì)觸發(fā)UI界面的更新
  • ref 復(fù)制它掂,修改響應(yīng)式數(shù)據(jù)不會(huì)影響以前的數(shù)據(jù)巴帮,數(shù)據(jù)變化UI更新
  • toRef 引用,修改響應(yīng)式數(shù)據(jù)會(huì)影響以前的數(shù)據(jù)虐秋,數(shù)據(jù)變化UI不更新
let obj={name:'zq',id:1}
let state=ref(obj.name);
function myFn(){
    /*
    如果利用ref將某一個(gè)對(duì)象中的屬性變成響應(yīng)式數(shù)據(jù)
    如果修改響應(yīng)式的數(shù)據(jù)是不會(huì)影響到原始數(shù)據(jù)的榕茧,即ref其實(shí)是復(fù)制的關(guān)系
    */
    state.value='zs';
    // obj  發(fā)現(xiàn)壓根沒變
    //state 響應(yīng)式對(duì)象是變化了
}


let obj={name:'zq',id:1}
let state=toRef(obj,'name');
function myFn(){
    state.value='zs';
    // obj/state  數(shù)據(jù)都變化了

    /**
     * 利用toRef將某一個(gè)對(duì)象中的屬性變成響應(yīng)式的數(shù)據(jù),修改響應(yīng)式的數(shù)據(jù)是會(huì)修改原始數(shù)據(jù)的
     *  但是不會(huì)觸發(fā)UI界面的更新
     * /
}

toRefs

  • 類似于toRef客给,但是是可以添加多個(gè)響應(yīng)的
  • toRef如果響應(yīng)多個(gè)用押,是需要多次調(diào)用的
let obj={name:'zq',id:1}
let state=toRefs(obj);
function myFn(){
    //注意此處value的位置
    state.name.value='zs';
    state.id.value=16;
}

customRef

  • 自定義ref
  • 應(yīng)用場(chǎng)景:setup函數(shù)內(nèi)部必須是同步操作,網(wǎng)絡(luò)請(qǐng)求一般都是異步的起愈,雖然回調(diào)可以使用只恨,但是不優(yōu)雅译仗,這時(shí)候自定義ref的場(chǎng)景(最起碼使用起來是同步的)就出現(xiàn)了
function myRef(value) {
  return customRef((track,trigger)=>{
    return {
      get(){
        track();//告訴vue,這個(gè)數(shù)據(jù)需要追蹤變化
        return value;
      },
      set(val){
        value=val;
        trigger();//告訴vue觸發(fā)界面更新
      }
    }
  })
}

 setup() {
    let age = myRef(18);
    function myFn() {
      age.value += 1;
    }
    return { age, myFn };
  },
  • 案例:網(wǎng)絡(luò)請(qǐng)求
function myRef(value) {
  return customRef((track, trigger) => {
    fetch(value)
      .then((res) => {
        return res.json();
      })
      .then((data) => {
        value = data;
        trigger();
      })
      .catch((err) => {
        console.log(err);
      });
    return {
      get() {
        track(); //告訴vue官觅,這個(gè)數(shù)據(jù)需要追蹤變化
        //注意點(diǎn):不能再get方法中發(fā)送網(wǎng)絡(luò)請(qǐng)求纵菌,因?yàn)殇秩窘缑?>調(diào)用get->發(fā)送網(wǎng)絡(luò)請(qǐng)求->保存數(shù)據(jù)->更新界面->調(diào)用get;形成死循環(huán)
        return value;
      },
      set(val) {
        value = val;
        trigger(); //告訴vue觸發(fā)界面更新
      },
    };
  });
}

setup() {
    //使用的時(shí)候,同步使用即可
    let data = myRef("../public/data.json");
    return { data };
  },

組合API中監(jiān)聽生命周期

<template>
  <div>
    <button ref="btn">添加</button>
  </div>
</template>

<script>
import { onMounted, ref } from "vue";


export default {
  name: "HelloWorld",
  setup() {
    let btn=ref(null);

    //組合API中監(jiān)聽生命周期
    onMounted(()=>{
      console.log('onMounted',btn.value); //<button>添加</button>
    })
    return { btn };
  },
};
</script>

readonly

傳入一個(gè)對(duì)象(響應(yīng)式或普通)或ref,返回一個(gè)原始對(duì)象的只讀代理休涤,這個(gè)只讀代理是深層的咱圆,對(duì)象內(nèi)部任何嵌套的屬性也都是只讀的。

  • 只讀數(shù)據(jù)
  • isReadonly:判斷數(shù)據(jù)類型是不是readonly/shallowReadonly類型
  • const :賦值保護(hù)功氨,不能給變量重新賦值序苏,但是對(duì)象內(nèi)部修改可以修改
  • readonly:屬性保護(hù),不能給屬性重新賦值
  • 注意: shallowReadonly :創(chuàng)建一個(gè)只讀的數(shù)據(jù)捷凄,第一層只讀
setup() {
    //用于創(chuàng)建一個(gè)只讀的數(shù)據(jù)忱详,并且是遞歸只讀(全部只讀)
    let state = readonly({ name: "zq" });
    // state.name='asfsa' //警告:Set operation on key "name" failed: target is readonly
    return { state };
  },

手寫Reactive/Ref等等

function shallowReactive(obj) {
    return new Proxy(obj, {
        get(ojb, key) {
            return obj[key];
        },
        set(obj, key, val) {
            obj[key] = val;
            return true;
        }
    })
}

function shallowReadonly(obj) {
    return new Proxy(obj, {
        get(ojb, key) {
            return obj[key];
        },
        set(obj, key, val) {
            console.log(`${key}是只讀的,不能賦值`);
        }
    })
}
//同理readonly其實(shí)就是把reactive的set改為輸出即可

function shallowRef(val) {
    return shallowReactive({ value: val });
}
function ref(val) {
    return reactive({value:val})
}

function reactive(obj) {
    if (typeof obj === 'object') {
        if (obj instanceof Array) {
            //如果是數(shù)組跺涤,取出數(shù)組中每一個(gè)元素匈睁,判斷每個(gè)元素是否是對(duì)象
            //如果又是對(duì)象也需要包裝成proxy
            obj.forEach(item => {
                if (typeof item === 'object') {
                    obj[index] = reactive(item);
                }
            })
        } else {
            //如果是對(duì)象,取出對(duì)象屬性的取值桶错,判斷對(duì)象屬性的取值是否又是對(duì)象航唆,如果還是對(duì)象則包裝成proxy
            for (const key in obj) {
                let item = obj[key];
                if (typeof item === 'object') {
                    obj[key] = reactive(item);
                }
            }
        }
        return new Proxy(obj, {
            get(ojb, key) {
                return obj[key];
            },
            set(obj, key, val) {
                obj[key] = val;
                return true;
            }
        })
    } else {
        console.log(`${obj} is not a object`);
    }
}

TS搭建vue3.0開發(fā)環(huán)境

  • 方案一:初始化項(xiàng)目時(shí)候選擇自定義
  • 方案二:進(jìn)入已有項(xiàng)目,執(zhí)行`vue add typescript

主要變化

  • script標(biāo)簽添加ts
  • export default后添加defineComponent

<script lang="ts">
import { defineComponent } from 'vue';
import  Home  from "./components/Home.vue";
import  News  from "./components/News.vue";

export default defineComponent({
  name: 'App',
  components: {
    Home,
    News
  },
  data(){
    return{
      aaa:'ada'
    }
  }
});
</script>

基本使用

<template>
    <p>{{title}}</p>
    <button @click="setTitle">修改</button>
</template>

<script lang="ts">
import { defineComponent } from "vue";
let title:string="hahahh";
export default defineComponent({
    data(){
        return {
            title
        }
    },
    methods:{
        setTitle(){
            this.title="修改呢";
        }
    }
})
</script>

使用接口限制數(shù)據(jù)類型

<template>
<p>----{{username}}----</p>
<button @click="setUserName">改變</button>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
interface User{
    username:string,
    age:number,
    setUserName():void;
    getUserName?():void;
}
export default defineComponent({
    setup(){
        //實(shí)現(xiàn)接口的第一種寫法
        let user:User=reactive({
            age:20,
            username:"張三",
            setUserName(){
                this.username="修改名字"
            }
           
        })
        //實(shí)現(xiàn)接口的第二種寫法
        // let user=reactive<User>({
        //     age:20,
        //     username:"張三",
        //     setUserName(){
        //         this.username="修改名字"
        //     }
           
        // })
        //實(shí)現(xiàn)接口的第三種寫法
        // let user=reactive({
        //     age:20,
        //     username:"張三",
        //     setUserName(){
        //         this.username="修改名字"
        //     }
           
        // }) as User

        //普通寫法
        // let user=reactive({
        //     age:20,
        //     username:"張三",
        //     setUserName(){
        //         this.username="修改名字"
        //     }
        // })

        //泛型
        // let count=ref<number|string>("20");
        return{
            ...toRefs(user)
        }
    }
})
</script>

路由

npm install vue-router@next -S

  • 新建src/routes.ts配置路由
import { createRouter,createWebHashHistory,createWebHistory  } from "vue-router";
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import NewsContent from "./components/NewsContent.vue";
const router=createRouter({
    history:createWebHashHistory(),//hash模式
    // history:createWebHistory(),//history模式
    routes:[
        {path:'/',component:Home},
        {path:'/news',component:News},
        {path:'/newscontent/:aid',component:NewsContent},//動(dòng)態(tài)路由
    ]
})
export default router;

獲取$router

import {getCurrentInstance} from 'vue'
setup(){
  //const router=useRouter();//vue-router中的useRouter直接獲取router對(duì)象
  const {ctx}=getCurrentInstance();
  ctx.$router.push('/about);

  //或者直接:router.push('/about);
}

//獲取動(dòng)態(tài)路由參數(shù)
setup(){
  const router=useRouter();
  //或者ctx形式也行
  console.log(router.currentRoute.value.params.id);
}
  • main.ts
import { createApp } from 'vue'
import App from './App.vue'
import route from "./routes";
const app=createApp(App);
app.use(route);
app.mount('#app');

<template>
  <router-link to="/">首頁</router-link>
  <router-link to="/news">新聞</router-link>
  <router-view></router-view>
</template>
  • News.vue
<template>
<ul>
    <li v-for="(item,index) in list" :key="index">
    <!--路由跳轉(zhuǎn)-->
        <router-link :to="`/newscontent/${index}`">{{item}}</router-link>
    </li>
</ul>
</template>

<script lang="ts">
import { defineComponent} from "vue";
interface User{
    list:string[]
}
export default defineComponent({
    data(){
        return {
            list:[]
        } as User;
    },
    mounted(){
        for (let i = 0; i < 10; i++) {
            this.list.push(`我是第${i}個(gè)新聞`)
        }
    }
})
</script>
  • NewsContent.vue
<template>
<p>NewContents組件</p>
</template>

<script lang="ts">
import { defineComponent} from "vue";

export default defineComponent({
   mounted(){
       //獲取動(dòng)態(tài)路由傳值
       console.log(this.$route.params);
   }
})
</script>
  • Get獲取參數(shù)
get參數(shù)傳值:http://localhost:8080/#/newscontent?aid=1230
獲取: this.$route.query 
  • 編程式路由
this.$router.push({path:'news'})
this.$router.push({path:'/newscontent/495'})

Vuex

setup(){
  //const store =useStore();//vuex中的useStore直接獲取store對(duì)象
  const {ctx}=getCurrentInstance();
  const storeCount=computed(()=>ctx.$store.state.count);
  add(){
    ctx.$store.commit('addMutation');
  }
  return{ storeCount}
}

//store/index.js
export default Vuex.createStore({
  state:{count:1},
  mutations:{
    addMutation(state){
      state.count++;
    }
  },
  actions:{

  },
  modules:{

  }
})

不能使用mapMutations,mapState....,因?yàn)橐蕾囉趖his.$store;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末院刁,一起剝皮案震驚了整個(gè)濱河市糯钙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌退腥,老刑警劉巖任岸,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異狡刘,居然都是意外死亡演闭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門颓帝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窝革,你說我怎么就攤上這事购城。” “怎么了虐译?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵瘪板,是天一觀的道長。 經(jīng)常有香客問我漆诽,道長侮攀,這世上最難降的妖魔是什么锣枝? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮兰英,結(jié)果婚禮上撇叁,老公的妹妹穿的比我還像新娘。我一直安慰自己畦贸,他們只是感情好陨闹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著薄坏,像睡著了一般趋厉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胶坠,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天君账,我揣著相機(jī)與錄音,去河邊找鬼沈善。 笑死乡数,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的矮瘟。 我是一名探鬼主播瞳脓,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼澈侠!你這毒婦竟也來了劫侧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤哨啃,失蹤者是張志新(化名)和其女友劉穎烧栋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拳球,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡审姓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祝峻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魔吐。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖莱找,靈堂內(nèi)的尸體忽然破棺而出酬姆,到底是詐尸還是另有隱情,我是刑警寧澤奥溺,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布辞色,位于F島的核電站,受9級(jí)特大地震影響浮定,放射性物質(zhì)發(fā)生泄漏相满。R本人自食惡果不足惜层亿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望立美。 院中可真熱鬧匿又,春花似錦、人聲如沸悯辙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躲撰。三九已至针贬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拢蛋,已是汗流浹背桦他。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谆棱,地道東北人快压。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像垃瞧,于是被迫代替她去往敵國和親蔫劣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容