記一次vue3.0技術分享會

記一次vue3.0技術分享會

記錄了組內技術分享會, 有一樣需求的同窗能夠參考一下
分享全程下來時間大約1小時

一. 版本

當前還處於 beta版本, 想要正式使用在項目裏還須要一段的時間, 可是結構與api變化應該不大了.前端

這裏列出的並不全, 可是夠用了
1. alpha 內測版本
2. beta  公測版本
3. Gamma 正式發佈的候選版本
4. Final 正式版
5. plus  加強版
6. full  徹底版 
7. Trial 試用版(通常有時間或者功能限制)

二. 介紹

1. 學一門新鮮的技術,就像練習王者榮耀新出的英雄同樣, 探索他的好玩之處能夠給開發者帶來快樂, 使用新的好的技術會讓工做更愉悅

  2. 這個版本的vue 相似"個人世界", 所有都是一個個方塊組成, 不要小看方塊, 方塊多了甚至能夠組成圓形(量變引發質變), 新鮮的玩法才能激發咱們的興趣

三. vue3.0的環境搭建

準備一套搭建好的環境防治到時候出現意外, 現場再按照步驟搭建一版, 每一步都不要落下能讓你們更好的理解.
1. npm install -g @vue/cli  cli升級到4版本

  2. 隨便建立個項目, vue create next-test

  3. 選擇配置最好把router與vuex一塊兒選上, 方便後續升級

  4. vue add vue-next   cli提供的進化到下一版本的命令, 執行後自動將router, vuex 升級到alpha版.

四. vue3.0重要的優化

1. 模板編譯速度的提高, 對靜態數據的跳過處理.
  2. 對數組的監控
  3. 對ts有了很好的支持
  4. 對2.x版本的徹底兼容
  5. 能夠有多個根節點 (也有bug, 好比外層開了display:flex 那麼裏面會收到影響, 也就是說佈局更靈活但也更要當心, 總之請對本身與他人的代碼負責)
  6. 支持Source map, 雖然沒演示可是這點真的重要

五. vuex, router, vue 初始化寫法的變化

vue:
import { createApp } from 'vue';
import App from './App.vue'
import router from './router'
import store from './store'

// 方法一. 建立實例變成了鏈式, 直接寫下去感受語義與結構有點模糊, 可是咱們要理解vue這樣作的良苦用心, 前端趨近於函數化.
// createApp(App).use(router).use(store).mount('#app')

// 方法二.
const app = createApp(App);
app.use(router);
app.use(store);
app.mount('#app');
vuex:
import { createStore } from 'vuex'
// 專門建立實例的一個方法
export default createStore({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
});
router
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  }
]

const router = createRouter({
// 專門建立history的函數
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

六. 變量的定義

1: refvue

import { ref } from "vue";
export default {
  // 1: 這個版本基本邏輯都在setup裏面完成了, 有人說把他當成2.x的data.
  setup() {
    // 2: 定義一個追蹤的變量,也就是雙向綁定.
    const n = ref(1); // 生成的n是一個對象, 這樣方便vue去監控它
    function addN() {
      n.value++; // 注意這裏要使用.value的形式, 由於n是對象↑, value纔是他的值
    }
    return {
      n,   // 返回出去頁面的template纔可使用它, {{n}} 不須要.value
      addN
    }
  }
 }

2: reactivereact

import { reactive, toRefs } from "vue";
export default {
  setup() {
    // 注意事項: reactive的對象不能夠結構返回或導入, 會致使失去響應式
    const obj = reactive({
      name: "金毛",
      age: 4
    });
    function addObj() {
      obj.age++;
    }
    return {
      ...obj, // 這樣寫很差, 裏面會失去響應式
      obj, // 這樣寫那麼外面就要都基於obj來調取, 類型{{obj.age}}
      ...toRefs(obj) // 必須是reactive生成的對象, 普通對象不能夠, 他把每一項都拿出來包了一下, 咱們能夠這樣用了 {{age}}, 放心我們多深的obj也能夠響應
    }
  }
 }

7. 以前的ref何去何從

這個老兄被別人搶了關鍵詞, 也只能本身改改寫法了
<div>
    <div ref="content">第一步, 在dom上面定義, 他會有一個回調</div>
  </div>
  <ul>
    <li>v-for 出來的ref</li>
    <li>能夠寫爲表達式的形式, 能夠推導出vue是如何實現的</li>
    <li>vue2.x的時候v-for不用這麼麻煩, 直接寫上去會被組裝成數組</li>
    <li :ref="el => { items[index] = el }" v-for="(item,index) in 6" :key="item">{{item}}</li>
  </ul>
import { ref, onMounted, onBeforeUpdate } from "vue";
export default {
  setup() {
    // 2: 定義一個變量接收dom, 名字無所謂, 可是與dom統一的話會有很好的語義化
    const content = ref(null);
    const items = ref([]);

    // 4: 在生命週期下, 這個值已經完成了變化, 因此固然第一時間就拿到
    onMounted(() => {
      console.log(content.value);
      console.log("li標籤組", items.value);
    });

    // 5: 確保在每次變動以前重置引用
    onBeforeUpdate(() => {
      items.value = [];
    });

    // 3: 返出去的名稱要與dom的ref相同, 這樣就能夠接收到dom的回調
    return {
      content,
      items
    };
  }
};

8. 生命週期

<template>
  <div>
    <button @click="add">點擊增長, 觸發updata</button>
    <p>{{obj.count}}</p>
  </div>
  <p>
    2.x與 3.0的對照
    beforeCreate -> 使用 setup()
    created -> 使用 setup()
    beforeMount -> onBeforeMount
    mounted -> onMounted
    beforeUpdate -> onBeforeUpdate
    updated -> onUpdated
    beforeDestroy -> onBeforeUnmount
    destroyed -> onUnmounted
    errorCaptured -> onErrorCaptured
  </p>
</template>

<script>

//這些生命週期註冊方法只能用在 setup 鉤子中
import { onMounted, onUpdated, onBeforeUnmount, reactive } from "vue";
export default {
  // 1: setup顯得冗長, 能夠本身動手作一些插件來優化
  // 2: 自己更加明確意圖
  // 3: 須要樹立工程師的正確代碼意識
  // 4: 能力不足可能會寫出更差的代碼
  // 做者說: 提高上界的收益遠遠大於下降下界的損失。值得深思, 前端須要提升門檻
  // 5: 調用時機: 建立組件實例,而後初始化 props ,緊接着就調用setup 函數。從生命週期鉤子的視角來看,它會在 beforeCreate 鉤子以前被調用
  // 6: 這些生命週期鉤子註冊函數只能在 setup() 期間同步使用, 由於它們依賴於內部的全局狀態來定位當前組件實例(正在調用 setup() 的組件實例), 不在當前組件下調用這些函數會拋出一個錯誤。
  // 7: 原則上生命週期裏面不會放具體的邏輯,哪怕只有一句賦值一個三元都不可放, 這也正好符合當前的工程模式

  // 討論: 有多少種方式, 能夠判斷出某個函數 當前處於哪一個函數?
  //       好比有多層嵌套的組件是否有影響
  setup() {
    onMounted(() => {
      console.log("is mounted!");
    });
    onUpdated(() => {
      console.log("is onUpdated!");
    });
    onBeforeUnmount(() => {
      console.log("is onBeforeUnmount!");
    });
    const obj = reactive({
      count: 1
    });
    function add() {
      obj.count++;
    }
    return {
      obj,
      add
    };
  }
};
</script>

9. 路由

<template>
  <div>
    {{id}}
  </div>
</template>

<script>
import { getCurrentInstance, ref } from 'vue';
export default {
  setup(){
    const { ctx } = getCurrentInstance()
    // 1. 這樣也是爲了去掉this
    // 2. 方便類型推導
    console.log(ctx.$router); // push等方法
    console.log(ctx.$router.currentRoute.value); // 路由實例
    // 這個其實沒有必要變成ref由於這個值不必動態
    // 可是他太長了, 這個真的不能忍
    const id = ref(ctx.$router.currentRoute.value.query.id)

    // 4: 頁面攔截器
    ctx.$router.beforeEach((to, from,next)=>{
      console.log('路由的生命週期')
      next()
    })
    return {
      id
    }
  }
}

</script>

10. vuex

import { createStore } from 'vuex'

// 難道前端趨勢只有函數這一種嗎
export default createStore({
  state: {
    name:'牛逼, 你拿到我了',
    age: 24,
    a:'白',
    b:'黑'
  },
  mutations: {
    updateName(state, n){
      state.name += n
    }
  },
  actions: {
    deferName(store) {
     setTimeout(()=>{
       // 必須只有commit能夠修改值, 這個設定我比較反對, 能夠討論
       // vuex自己結構就很拖沓, 定義域使用我的都不喜歡
      store.state.name = '牛逼, 你改回來了'
     },1000)
    }
  },
  getters: {
    fullName(state){ return `${state.a} - + -${state.b}` }
  },
  modules: {
  }
});
<template>
  <div>
    <p>{{name}}</p>
    <button @click="updateName('+')">點擊改變名字</button>
    <br />
    <button @click="deferName('+')">改回來</button>

    <p>{{fullName}}</p>
  </div>
</template>

<script>
import { useStore } from "vuex";
import { computed } from "vue";
export default {
  setup() {
    const store = useStore();
    // 1: 單個引入
    const name = computed(() => store.state.name);
    // 2: 引入整個state
    const state = computed(() => store.state);
    console.log("vuex的實例", state.value); // 別忘了.value

    // 3: 方法其實就直接從本體上取下來了
    const updateName = newName => store.commit("updateName", newName);

    // 4: action一個意思
    const deferName = () => store.dispatch("deferName");

    // 5: getter 沒變化
    const fullName = computed(() => store.getters.fullName);
    return {
      name,
      fullName,
      deferName,
      updateName,
    };
  }
};
</script>

11. composition(這個多是最重要的改革了)

前端算是面向函數編程, 各類規範也都趨近於函數化
composition使得前端工程師的編程規範, 更接近於原生js, 三十年河東三十年河西, 幾年前前端須要模板來進行'規則化', 如今前端又想要更多的自由.
開發工程而不是插件的話, 仍是不要使用mixin了, 這東西沒法追溯來源, 搞得語義太差了, 咱們要對它說'no'.
舉例子的變量命名有點low, 抱歉~~
<template>
  <div>
    <button @click="addN1">上面的增長</button>---> {{n1}}
  </div>
   <div>
    <button @click="addN2">下面的增長</button>---> {{n2}}
    <button @click="addN210">每次n2+10</button>
  </div>
  <div>
    <p>組件裏面也能夠如此引用, 這就能夠代替mixin一部分功能了</p>
    <button @click="addN3">n3的增長</button>---> {{n3.value}}
  </div>
  <div>
    <com></com>
  </div>
</template>

<script>
import { ref} from 'vue';
import n3Change from './mixin';
import com from '../components/composition.vue';

export default {
  components:{
    com
  },
  setup(){
     // 1: setup只是一個整合函數
     // 2: 甚至整個函數裏面可能會沒有具體的邏輯
     // 3: 以此推斷, ref等方式定義的變量, 會自動識別在哪一個setup內部, 從而達到邏輯的複用
     // 4: 由此方法能夠很好的代替mixin了
     // 5: 固然, 這裏也能夠截獲數據,來作一些事情
     const {n2, addN2} = n2Change();
     function addN210(){
       n2.value += 10
     }
     return {
       ...n1Change(),
       ...n3Change(),
       n2,
       addN2,
       addN210
     }
  }
}
// 甚至已經能夠寫在任何地方了, 響應式自由度大大提升
function n1Change(){
   const n1 = ref(1);
   let addN1 = ()=>{
     n1.value++
   }
   return {
     n1,
     addN1
   }
}

function n2Change(){
   const n2 = ref(1);
   let addN2 = ()=>{
     n2.value++
   }
   return {
     n2,
     addN2
   }
}
</script>

寫在任何地方, 而後導入就成了mixinios

import { reactive } from 'vue';


export default ()=> {
  const n3 = reactive({
    name: 'mixin',
    value: 1
  })
  const addN3=()=>{
    n3.value++
  }
  return {
    n3,
    addN3
  }
}

12. 插件的新思路

// 開發插件並不必定要掛載到vue的原型上
// 致使vue原型臃腫, 命名衝突等等(好比兩個ui都叫 message)
// 原理就是 provide 和 inject, 依賴注入.

import {provide, inject} from 'vue';

// 這裏使用symbol就不會形成變量名的衝突了, 這個命名權交給用戶纔是真正合理的架構設計
const StoreSymbol = Symbol()

export function provideString(store){
  provide(StoreSymbol, store)
}

export function useString() {

  const store = inject(StoreSymbol)

  return store
}

app.vue頁面統一的初始化一下vue-router

export default {
  setup(){
    // 一些初始化'配置/操做'能夠在這裏進行
    // 須要放在對應的根節點, 由於依賴provide 和 inject
     provideString({
       a:'可能我是axios',
       b:'可能我是一個message彈框'
     })
  }
}

在須要使用的組件裏面接收vuex

<template>
  <div>
    插件的演示
  </div>
</template>

<script>
import { useString } from '../插件';

export default {
  setup(){
    const store = useString();
    // 不光是拿到值, 能夠由app定義什麼能夠被拿到
    console.log('拿到值了',store)
  }
}

</script>

13. 新觀察者

<template>
  <div>
    <button @click="addn1">n1增長--{{n1}}</button>
    <button @click="addn2">n2增長--{{n2}}</button>
    <button @click="addn3">n3增長--{{n3}}</button>
  </div>
</template>

<script>
import { watch, ref } from "vue";
export default {
  setup() {
    const n1 = ref(1);
    const n2 = ref(1);
    const n3 = ref(1);
    // 1: 監聽一個
    // 第一個參數是函數返回值, 固然也能夠 直接寫n1 
    // 若是監聽的是一個對象裏面的某個屬性, 那就須要這種函數的寫法了, 比2.x的字符串寫法高明不少

    watch(
      () => n1.value,
      (val, oldVal) => {
        console.log("新值", val);
        console.log("老值", oldVal);
      }
    );
    // 2: 監聽多個
    // 數組的形式定義多個, 這就出現問題了吧, 若是我觀察的對象就是個數組, 而且每一項都是一個返回值的函數, 豈不是會被他誤認爲是多監控的結構, 苦惱
    watch(
      [() => n2.value, ()=>n3.value],
      ([val, val3],[val2, val4]) => {
        // val 是 n2的新值   val2是 n2的老值
        // val3 是 n3的新值  val4是 n3的老值
        console.log("新值 與 老值 是這種對應關係", val, val2);
        console.log("新值 與 老值 是這種對應關係", val3, val4);
      }
    );

    function addn1() {
      n1.value++;
    }
    function addn2() {
      n2.value++;
    }
     function addn3() {
      n3.value++;
    }
    return {
      addn1,
      addn2,
      addn3,
      n1,
      n2,
      n3
    };
  }
};
</script>

13. 新計算屬性

別看watchEffect帶個'watch',可是他的功能能夠歸爲計算屬性裏面
<template>
  <div>
    <button @click="addCount">點擊計算</button>
    <button @click="setCount(1)">點擊出發set</button>
    <p>count--{{count}}</p>
    <p>count2--{{count2}}</p>
    <p>count3--{{count3}}</p>
  </div>
</template>

<script>
// 弄得相似react了
import { computed, ref, watchEffect } from "vue";
export default {
  setup() {
    const count = ref(1);
    // 1. 默認的定義方式
    const count2 = computed(() => count.value * 2);
    console.log(count2.value); // 也要value由於多是簡單類型
    // 2. getter與setter固然能夠定義
    const count3 = computed({
      get: () => count.value * 3,
      set: val => {
        // 這裏重置了count
        count.value = val;
      }
    });
    // 3. watchEffect 更像是計算函數
    // 當即執行傳入的一個函數,並響應式追蹤其依賴,並在其依賴變動時從新運行該函數
    // 偵聽器會被連接到該組件的生命週期,並在組件卸載時自動中止。
    // Vue 的響應式系統會緩存反作用函數,並異步地刷新它, 好比同時改變了count與conut4此時watchEffect只是執行一次
    // 初始化運行是在組件 mounted 以前執行的。所以,若是你但願在編寫反作用函數時訪問 DOM(或模板 ref),請在 onMounted 鉤子中進行
    // 並非返回值, 而是監聽裏面全部的值, 任何有變化都會從新執行, 他應該能夠玩出點東西。
    const count4 = ref(1);
    const stop = watchEffect(() => {
      if (count4.value) {
        console.log("做爲判斷條件也能夠根據count4的變化而從新執行");
      }
      console.log(count.value);
    });
    setTimeout(() => {
      stop(); // 中止監聽
    }, 10000);
    function addCount() {
      count.value++;
      setTimeout(() => {
        count4.value++;
      }, 1000);
    }
    // 觸發setter
    function setCount() {
      count3.value = 2;
    }
    return {
      count,
      count2,
      addCount,
      count3,
      setCount
    };
  }
};
</script>

14. customRef防抖

固然這裏說的'防抖'不是重點, 重點是這種代碼的思惟
<template>
  <div>
    <input type="text" v-model="text" />
  </div>
</template>

<script>
import { customRef, onUnmounted } from "vue";
export default {
  setup() {
    let timeout = null; // 並不須要響應式
    const text = useDebouncedRef("hello", (time) => {
      // 畢竟是延時的不用擔憂獲取不到這個值
      console.log("延時執行回調", text.value);
      console.log('時間實例', time)
      timeout = time;
    });
    // 好習慣也是成爲合格工程師的必要條件
    onUnmounted(()=>{
        clearTimeout(timeout);
    })
    return {
      text
    };
  }
};

// 並不用純粹的js編寫, 能夠利用customRef來監控這個值的一舉一動
// 寫法通常, 可是思路又多了一條, 感謝
function useDebouncedRef(value, callback, delay = 200) {
  let timeout;
  // 兩個參數分別是用於追蹤的 track 與用於觸發響應的 trigger
  // 這兩個參數對 值的追蹤 在當前並無用,好比watchEffect的出發機制
  // 不調用這兩個值沒問題, 可是若是寫成插件的話仍是要調用的, 由於別人沒準在追蹤這個值,

  // 注意:  這個函數不能夠有太大的delay, 若是超過500的話就須要考慮在組件銷燬時候的清除定時器, 反而邏輯加深了, 此時咱們能夠每次把演示器的實例拿到
  return customRef((track,trigger) => {
    return {
      get() {
        track()
        return value;
      },
      set(newValue) {
        clearTimeout(timeout);
        // callback接受的太晚了, 能夠在這裏用另外一個函數或對象接收
        timeout = setTimeout(() => {
          value = newValue;
          trigger()
          callback(timeout);
        }, delay);
      }
    };
  });
}
</script>

15. 組件與注入

父級npm

<template>
  <div>
    組件:
    <zj :type="type" @ok="wancheng"></zj>
  </div>
</template>

<script>
import zj from "../components/子組件.vue";
import { ref } from 'vue';
import { provide } from 'vue'

export default {
  components: { 
    zj
  },
  setup() {
    provide('name','向下傳值'); // 基礎值
    provide('name2', ref('向下傳值')); // 監控值
    const type = ref('大多數');

    function wancheng(msg){
      console.log('子組件-->',msg)
      setTimeout(()=>{
        type.value = 'xxxxxxx'
      },2000)
    }
    return {
      type,
      wancheng
    }
  }
};
</script>

子組件編程

<template>
  <div>props的屬性不用setup去return --- {{type}}</div>
</template>

<script>
import { inject, ref } from 'vue'
// 爲了讓 TypeScript 正確的推導類型,咱們必須使用 createComponent 來定義組件:
export default {
  props: {
    type: String
  },
  // 1: props也是不能夠解構的, 會失去響應式
  // 2: context是上下文, 咱們能夠獲取到slots emit 等方法
  // 3: props, context 分開也是爲了ts更明確的類型推導
  // setup({type}){
  setup(props, context) {
    // 1: props
    console.log("props", props.type);
    console.log("上下文", context);
    context.emit('ok','傳遞完成')

    // 2: 注入
    console.log('inject',inject('name'));
    console.log('inject',inject('xxxx','我是默認值'))
    inject('name1', ref('默認值')) // 接收方也能夠這樣
  }
};
</script>

16. 總結

每次看到新技術都會感受挺好玩的, 一成不變的生活會太無趣了, 在某些方面講vue失去了一些原本的優點, 可是人家能夠兼容vue2.x那就沒的說了, 做爲分享會的稿子的話時間差很少一個小時, 最好每一個點都現場手敲, 光讓你們看已經寫好的代碼會走神的, 我在學習視頻的時候最不喜歡的就是老師說"這個我就不演示了".
此次就這麼多, 但願和你一塊兒進步.axios

相關文章
相關標籤/搜索