10 個超強 Vue3 實戰指南,由此突破新特性!

本篇介紹 10 點如何從實戰中學習突破 Vue JS 3 的新特性,細細看完,必定會有收穫~html

主體譯自:【Vue JS 3 — The Practical Guide】vue

更多學習資料:【https://github.com/Jerga99/vue-3-updates】react

目錄:git

  1. 初始化掛載
  2. Composition
  3. Data 選項
  4. Filters 被移除
  5. 多個根標籤
  6. Suspense
  7. 響應式
  8. 多個 v-model
  9. Teleport
  10. Vue Router

另:最近本瓜在參加 【掘金 2020 人氣創做者榜單打榜活動】,趕快來爲我 ——【掘金安東尼】投票吧!!!支持創做者!!!支持掘金!!!掘掘掘!!!github

你的點贊投票是我最大的動力,關注走一波,【2021】有更多好看~!😀😀😀web

初始化掛載

在 Vue2 中,咱們在 main.js 一般這樣進行初始化掛載:vue-router

new Vue({
  render: h => h(App),
  components: { App }
}).$mount('#app')
複製代碼

在 Vue3 中,調整爲這樣:api

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.mount('#app')
複製代碼

爲何發生這樣的變化?緣由是:若是咱們在 Vue2 中建立多個 Vue 實例,那麼全部應用(#app)都會共享全局相同的配置。數組

// 全局共享、相互影響
Vue.mixin({
  /* ... */
})

Vue.directive('...', {
  ...
})
const app1 = new Vue({ el: '#app-1' })
const app2 = new Vue({ el: '#app-2' })
複製代碼

顯然,這並非咱們想要的。在 Vue3 中,你能夠建立多個實例,且每一個實例均可以擁有單獨的配置。markdown

import { createApp } from 'vue'
import App1 from './App1.vue'
import App2 from './App2.vue'

const app1 = createApp(App1)
const app2 = createApp(App2)

app1.mount('#app-1')
app2.mount('#app-2')

app1.directive('...', {
  ...
})
app2.directive('...', {
  ...
})
複製代碼

可是,這也並不影響咱們設置共享全局配置,咱們能夠經過以下工廠函數方式實現:

import { createApp } from 'vue';
import App1 from './App1.vue';
import App2 from './App2.vue';

const createApp = (Instance) => {
    const App = createApp(Instance);
  App.directive('i-am-cool', {
      inserted: () => {
      console.log('I am cool!');
    },
  });
}

createIdolApp(App1).mount('#app-1');
createIdolApp(App2).mount('#app-2');
複製代碼

Composition API

受到 React 的啓發,Vue3 引入 Composition API 和 「hook」 函數。有了它,Vue 組件的使用變得更加靈活,也變得更增強大。特別是針對大型應用,如下會給出示例。

之前在 Vue2 中咱們是這樣寫組件的:獲取數據,設置數據。

<script>
import { fetchResources } from '@/actions'
import ResourceDetail from '@/components/ResourceDetail'
import ResourceList from '@/components/ResourceList'
export default {
  components: {
    ResourceDetail,
    ResourceList,
  },
  data() {
    return {
      title: 'Your resources',
      selectedResource: null,
      resources: []
    }
  },
  async created() {
    this.resources = await fetchResources()
  },
  computed: {
    hasResources() {
      return this.resourceCount > 0
    },
    activeResource() {
      return this.selectedResource || (this.hasResources && this.resources[0]) || null
    },
    resourceCount(){
      return this.resources.length
    }
  },
  methods: {
    selectResource(resource) {
      this.selectedResource = {...resource}
    }
  }
}
</script>
複製代碼

完整代碼在這裏

若是咱們使用 composition API,會是這樣寫:

import { ref, onMounted, computed } from 'vue'
import { fetchResources } from '@/actions'

export default function useResources() {
  const resources = ref([])
  const getResources = async () => resources.value = await fetchResources()

  onMounted(getResources);

  const resourceCount = computed(() => resources.value.length)
  const hasResources = computed(() => resourceCount.value > 0 )

  return {
    resources,
    resourceCount,
    hasResources
  }
}
複製代碼

這是一個很是簡單的 Composition function 實現了獲取 resources 數據的功能。

Composition 函數一般用 use 開頭做爲關鍵字,好比此處的 「useResources」,以此區別於普通函數。

下面針對以上代碼關鍵點進行一一釋義:

1. ref 會建立一個動態對象。若是你要從 ref 獲取原始值,則須要取 「value」 屬性,好比 —— resources.value

看如下示例:

var a = 7;
var b = a;
b = 10;
// a = 7
// b = 10

var a = ref(7);
var b = a;
b.value = 100;
// a = 100
// b = 100
複製代碼

咱們將返回的 resoure 數組設置爲 ref。是由於若是數組有新增項或移除項,這樣作能在程序中有所表現。

一圖勝萬言:

2. getResources 函數用於獲取數據。

3. onMounted 生命週期函數會在組件添加到 Dom 時調用。

4. computed 屬性會隨着它的依賴(resources or resourceCount)變化而從新計算。

5. return 最後一步咱們將返回 data/function,咱們再向組件暴露 useResource hook 函數以供使用。

最終 hook-in:

<script>
import ResourceDetail from '@/components/ResourceDetail'
import ResourceList from '@/components/ResourceList'
import useResources from '@/composition/useResources';
export default {
  components: {
    ResourceDetail,
    ResourceList,
  },
  data() {
    return {
      title: 'Your resources',
      selectedResource: null
    }
  },
  setup() {
    return {
      ...useResources() // 在 setup 裏
    }
  },
  computed: {
    activeResource() {
      return this.selectedResource || (this.hasResources && this.resources[0]) || null
    }
  },
  methods: {
    selectResource(resource) {
      this.selectedResource = {...resource}
    }
  }
}
</script>
複製代碼

咱們再 setup 中進行引用,返回值均可以再經過 this 進行調用。

咱們在 computed 和 methods 也能一樣進行調用 Composition 函數的返回。

注意:setup 鉤子函數執行在組件實例建立(created)以前。

在組件建立前 setup 中 hook 被執行,只要 props 被解析,服務就會以 composition API 做爲入口。由於此時當 setup 執行時,組件實例還未生成,沒有 this 對象。

神奇嗎?咱們就這樣將獲取數據進行封裝而後應用到了 hook 調用中。

再寫一個對 resources 資源進行搜索過濾的功能:

  • useSearchResource
import { ref, computed } from 'vue'

export default function useSearchResource(resources) {
  const searchQuery = ref('')

  const setSearchQuery = searched => {
    searchQuery.value = searched
  }

  const searchedResources = computed(() => {
    if (!searchQuery.value) {
      return resources.value
    }

    const lcSearch = searchQuery.value.toLocaleLowerCase();

    return resources.value.filter(r => {
      const title = r?.title.toLocaleLowerCase()
      return title.includes(lcSearch)
    })
  })

  return {
    setSearchQuery,
    searchedResources
  }
}
複製代碼
  • useResources
export default function useResources() {
  const resources = ref([])
  const getResources = async () => resources.value = await fetchResources()

  onMounted(getResources);

  const resourceCount = computed(() => resources.value.length)
  const hasResources = computed(() => resourceCount.value > 0 )

  const { searchedResources, setSearchQuery } = useSearchResources(resources)

  return {
    resources: searchedResources,
    resourceCount,
    hasResources,
    setSearchQuery
  }
}
複製代碼

拆解分析:

  1. searchQuery 包含一個空字符串,使用了 ref,computed searchedResources 能夠檢測 searchQuery 的變化值。
  2. setSearchQuery 是一個簡單的賦值給 searchQuery 的函數。
  3. searchedResources會在 searchQuery 或 resources 變化的時候觸發。
  4. searchedResources 負責過濾 resources。每一個 resource 包含一個 title,若是 title 包含 searchedQuery 字符串,那麼 resource 就會被加入到 searchedResources 數組中。
  5. 最後函數返回 setSearchQuerysearchedResourced,再在 useResources 中進行引用及返回。

再看下在組件中使用它到底有多簡單:

<template>
...
<input
  @keyup="handleSearch"
  type="text"
  class="form-control"
  placeholder="Some title" />
...
</template>
<script>
...
methods: {
  ...
  handleSearch(e) {
    this.setSearchQuery(e.target.value)
  }
}
...
</script>
複製代碼

真有你的! 不管什麼時候執行 setSearchQuery 更改 searchQuery 的值,都將從新執行 searchedResources 將其進行過濾操做並返回結果。

以上即是超重要的新特性 composition API 的實戰介紹。

Data 選項

在 Vue2 中,data選項不是對象就函數,可是在 Vue3 中將只能是函數。這將被統一成標準。

<script>
  export default {
    data() {
      return {
        someData: '1234'
      }
    }
  }
</script>
複製代碼

Filters 被移除

不會再出現這樣的寫法:

<h1>{{title | capitalized }} </h1>
複製代碼

這樣的表達式不是合法有效的 Javascript,在 Vue 中實現這樣的寫法須要額外的成本。它能夠很容易被轉化爲計算屬性或函數。

computed: {
 capitalizedTitle() {
   return title[0].toUpperCase + title.slice(1);
  }
}
複製代碼

多個根標籤

在 Vue2 中咱們常常這樣包裹咱們的根標籤:

<template>
  <div>
    <h1>...</h1>
    <div class="container">...</div>
    ...
  </div>
</template>
複製代碼

在 Vue3 中將再也不有此限制:

<template>
  <h1>...</h1>
  <div class="container">...</div>
  ...
</template>
複製代碼

Suspense

Suspense 是一種特殊的 Vue 組件,用於解析有異步數據的組件。

好比:

<Suspense>
  <template #default>
    <AsyncComponent>
  </template>
  <template #fallback>
    Loading Data...
  </template>
</Suspense>
複製代碼

使用新的 Composition API,setup 可設置異步函數。Suspense 能夠展現異步的模板直到 setup 被解析完。

實戰代碼:

<template>
  Welcome, {{user.name}}
</template>
<script>
  import { fetchUser } from '@/actions';
  export default {
    async setup() {
      const user = await fetchUser();
      return { user }
    }
  }
</script>
複製代碼
<Suspense>
  <template #default>
    <UserPanel/>
  </template>
  <template #fallback>
    Loading user ...
  </template>
</Suspense>
複製代碼

響應式

婦孺皆知,Vue3 的響應式也有很大變化(proxy),再也不須要使用 Vue.set 或 Vue.delete。你可使用簡單的原生函數來操做數組或對象。

// in composition API
const resources = ref([])
const addResource = newResource =>      resources.value.unshift(newResource)
const removeResource = atIndex => resources.value.splice(atIndex ,1)
const reactiveUser = ref({name: 'Filip'})
const changeName = () => reactiveUser.value.name = 'John'
複製代碼

之前則是須要這樣:

<script>
export default {
  data() {
    return {
      resources: [1,2,3],
      person: { name: 'Filip' }
    }
  }
  methods: {
    addResource(newResource) {
      this.resources.unshift(newResource)
    },
    removeResource(atIndex) {
      this.resources.splice(atIndex ,1)
    },
    changeName(newResource) {
      this.person.name = 'John'
    }
  }
}
</script>
複製代碼

在 composition API 中,全部的更改都是響應式的。

多個 v-model

在 Vue3 中,你可使用多個 v-model,好比這樣:

<ChildComponent v-model:prop1="prop1" v-model:prop2="prop2"/>
複製代碼

能夠實現如下代碼邏輯:

<ChildComponent
  :prop1="prop1"
  @update:prop1="prop1 = $event"
  :prop2="prop2"
  @update:prop2="prop2 = $event"
/>
複製代碼

看一個具體的例子:

<resource-form
  v-model:title="title"
  v-model:description="description"
  v-model:type="type"
  v-model:link="link"
  @on-form-submit="submitForm"
/>
複製代碼

form 組件是這樣:

<template>
  <form>
    <div class="mb-3">
      <label htmlFor="title">Title</label>
      <input
        :value="title"
        @input="changeTitle($event.target.value)"
        type="text" />
    </div>
    <div class="mb-3">
      <select
        :value="type"
        @change="changeType($event.target.value)">
        <option
          v-for="type in types"
          :key="type"
          :value="type">{{type}}</option>
      </select>
    </div>
    <button
      @click="submitForm"
      class="btn btn-primary btn-lg btn-block"
      type="button">
      Submit
    </button>
  </form>
</template>
export default {
  props: {
    title: String,
    description: String,
    link: String,
    type: String,
  },
  data() {
    return {
      types: ['blog', 'book', 'video']
    }
  },
  methods: {
    submitForm() {
      this.$emit('on-form-submit');
    },
    changeTitle(title) {
      this.$emit('update:title', title)
    },
    changeType(type) {
      this.$emit('update:type', type)
    }
    ...
  }
}
複製代碼

咱們綁定多個 input 值,監聽變化,再emit 來更新值父組件的值。注意這裏寫法的格式。

Teleport

提供如何在當前上下文以外只呈現模板的一部分的方法。

要「teleport」內容,咱們須要使用 teleport 組件並把內容包在裏面,以下:

<teleport to="#teleportContent">
  <div class="teleport-body">I am Teleported!</div>
</teleport>
複製代碼

此內容將被傳送到 id 爲 teleportContent 的節點中

<div id="teleportContent"></div>
複製代碼

惟一的條件是:在定義傳送內容以前,傳送到的目標節點需已經存在。

咱們能夠綁定 id,類,[data-xx]。

<!-- ok -->
<teleport to="#some-id" />
<teleport to=".some-class" />
<teleport to="[data-teleport]" />

<!-- Wrong -->
<teleport to="h1" />
<teleport to="some-string" />
複製代碼

Vue Router

Vue2 中咱們這樣設置及綁定路由:

import Vue from 'vue'
import VueRouter from 'vue-router';

import SomePage1 from './pages/SomePage1';
import SomePage2 from './pages/SomePage2';

Vue.use(VueRouter);

const routes = [
  { path: '/', redirect: '/path' },
  { path: '/path', name: 'HomePage', component: SomePage1 },
  { path: '/some/path', component: SomePage2 }
]

const router = new VueRouter({
  mode: 'history',
  linkExactActiveClass: 'active',
  routes
})

export default router;
複製代碼

main.js

import router from './router'; 
new Vue({render: h => h(App),  
  router,  
  components: { App }})
.$mount('#app')
複製代碼

在 Vue3 中:

import { createRouter, createWebHistory } from 'vue-router'
import SomePage1 from './pages/SomePage1'
import SomePage2 from './pages/SomePage2'

const routes = [
  { path: '/', redirect: {name: 'HomePage'}},
  { path: '/path', name: 'HomePage', component: SomePage1 },
  { path: '/some/path', component: SomePage2 }
]

const router = createRouter({
  history: createWebHistory(),
  linkExactActiveClass: 'active',
  routes
})

export default router;
複製代碼

main.js

import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
複製代碼

小結

此篇總結了 Vue3 的一些主要功能,都是從實戰出發來突破 Vue3 的新特性。實踐是檢驗真理的惟一標準!Vue3 究竟強不強?有多強?市場和時間會告訴咱們答案!從現階段看,早學早享受QAQ~若是想要了解更多,請查閱 官方文檔

我是掘金安東尼,持續輸出ING......點贊👍 + 收藏📕 + 關注👀,咱們下期再會~

相關文章
相關標籤/搜索