vue: vue3的优化 开始vue3项目

Vue 3.0 中文文档 链接 (现有用户直接到迁移指南 迁移链接)

支持vue3的组件库: Element-plus Vant 3.0 Ant-design-vue

vue2两个问题:对复杂组件难以维护;对typescript支持不够;(此时就可以用vue3)

  1. diff算法优化:
    在一个组件下,对有插值表达式和动态属性的dom做了标记。虚拟Dom的diff比较时就不用比较所有页面的所有的dom元素差异
    // 也就是跳过静态节点,只处理动态节点
  2. tree shaking (去掉了未使用的api):
    不适用不会引入v-model,transition相关代码

1)Composition API
90%的vue2(optionsAPI)代码在vue3(Composition API)中不受影响
// 能用compositioin抽逻辑就不要用mixin,因为composition可以完全替代mixin
即组合使用reactive ref watchEffect这三个
2)Fragment碎片
不再限制单文件组件只能有一个根节点
3)teleport
4)suspense
5)完全支持ts,支持tsx。体验效果很好
// vue3.0 是由ts重写的
6)custom renderer API
// 自定义 render 函数的 API
7)vite热加载,相比webpack基本加载在100ms以内

  1. vue全家桶-改动
    1)vuex基本没有任何改动
    2)router有一些api的改动
    3)CLI: vue-cli-plugin-vue-next生成vue3项目
    4)vite
    实验性,快速生成单文件组件
    5)vue-test-utils
    // 在做vue3兼容
    6)Vetur组件库在跟进vue3.0更新
  2. 不建议生产项目中使用vue3-beta(可能影响项目的稳定性),可能需要等2020年终使用vue3

一: 使用vite初始化vue3项目

npm install create-vite-app --save-dev //也可以全局下载
npx create-vite-app project-name // 项目
npm install
npm run dev
npm run build
// 下载插件
npm i vue-router@next
npm i vuex@next
npm i vant@next -S

// src/srouter.js
import {
      createRouter, createWebHistory } from 'vue-router'
import Home from './components/home/Home.vue'

const router = createRouter({
     
  history: createWebHistory(),
  routes: [
    // route -> routes
    {
     
      path: '/',
      name: 'home',
      component: Home,
    }
  ],
})

export default router
// src/main.js
import {
      createApp } from 'vue'
import App from './App.vue'
import './index.css'

import router from './router'
// import store from './store' 

const app = createApp(App)
app.use(router)

app.mount('#app')

// app.vue
<template>
  <!-- <img alt="Vue logo" src="./assets/logo.png" /> -->
  <!-- <HelloWorld msg="Hello Vue 3.0 + Vite" /> -->
  <router-view> </router-view>
</template>

<script>
// import HelloWorld from './components/HelloWorld.vue'

export default {
     
  name: 'App',
  components: {
     
    // HelloWorld
  }
}
</script>

就可以创建一个vue3项目了

下载后,就使用vant组件库

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

import router from './router'

// 引入vant组件和全局样式
import {
      Button } from 'vant';
import 'vant/lib/index.css';

const app = createApp(App)
app.use(router)

 // 注册vant组件
app.use(Button)

app.mount('#app')

setup使用

<template>
  <div>
    home
  </div>
</template>

<script>
import {
     } from 'vue'
export default {
     
  name: 'home',
  setup () {
     
    // 在created(组件初始化)钩子前执行,因为拿不到created和methods所以setup中没有this;
    // 且setup函数只能是同步的,但是可以在setup中使用Composition API
    console.log('setup')
  },
  created(){
     
    console.log('created')
  }
}
</script>

二: 使用vue-cli创建vue3项目

npm i -g @vue/cli // vue-cli4.5以上的版本才能创建vue3(vue --version查看版本)
vue ui
--自定义预设-选择功能-选择vue3 previe,不要选class-创建

文件描述

  • ts的语言服务需要.d.ts文件来识别类型,这样才能做到相应的语法检查和智能提示

使用

–ref,computed


  <h1>{
     {
      count }}</h1>
  <h1>{
     {
      count2 }}</h1>
  <button @click="add">增加</button>
import {
      ref, defineComponent, computed } from 'vue'

setup() {
     
    const count = ref(0)
    const count2 = computed(() => {
     
      return count.value * 2
    })
    function add() {
     
      count.value++
    }
    return {
     
      count,
      add,
      count2
    }
  }

—reactive, toRefs

  <h1>{
     {
      count }}</h1>
  <h1>{
     {
      double }}</h1>
  <button @click="increase">增加</button>
import {
      computed, reactive, toRefs } from 'vue'

interface DataProps {
     
  count: number;
  double: number;
  increase: () => void;
}

setup() {
     
    const data: DataProps  = reactive({
     
      count: 0,
      increase: () => {
      data.count++},
      double: computed(() => data.count * 2),
    })
    const refData = toRefs(data) // toRefs可以使{}对象类型下每个属性具有响应性
    return {
     
      ...refData
    }
  }

–修改对象属性和数组索引也具有响应式

  <ul>
    <li v-for="(item, index) in list" :key="index">{
     {
      item }}</li>
  </ul>
  <div>{
     {
     obj.name}}</div>
import {
      reactive, toRefs } from 'vue'

interface DataProps {
     
  list: number[]
  obj: {
      name?: string }
}

  setup() {
     
    const data: DataProps = reactive({
     
      list: [1, 2, 3, 4],
      obj: {
     
      },
    })
    data.list[2] = 0
    data.obj.name = '张三'
    const refData = toRefs(data) // toRefs可以使{}对象类型下每个属性具有响应性
    return {
     
      ...refData,
    }
  }

–生命周期
vue: vue3的优化 开始vue3项目_第1张图片

import {
      ref, onMounted } from 'vue'

  setup () {
     
      onMounted(() => {
      
      // 初始化echarts
      var myChart = echarts.init(document.getElementById('main'));
      myChart.setOption(option);
    })
  }

(onRenderTracked, onRenderTriggered检测数据的钩子)

import {
      onMounted, onUpdated } from 'vue'
  setup() {
     
    onMounted(()=>{
     
      console.log('onMounted')
    })
    onUpdated(()=>{
     
      console.log('数据更新触发onUpdated')
    })
    return {
     
    }
  }

– watch

  <h1>{
     {
      count }}</h1>
  <h1>{
     {
      num }}</h1>
  <button @click="increase">增加count</button>
  <button @click="numFn">增加num</button>
  <button @click="updateTitle">更新</button>
import {
     
  ref,
  defineComponent,
  reactive,
  toRefs,
  watch,
} from 'vue'
interface DataProps {
     
  count: number
  increase: () => void
}
setup() {
     
    const data: DataProps = reactive({
     
      count: 0,
      increase: () => {
     
        data.count++
      },
    })
    let num = ref('')
    function numFn() {
     
      num.value = 'abc'
    }
    function updateTitle() {
     
      watch([num, () => data.count], (newOld, oldVal) => {
     
        // 直接写是具有响应式的属性 // 函数形式:可以监控对象内的一个属性
        document.title = 'update' + data.count
      })
    }
    const refData = toRefs(data) // toRefs可以使{}对象类型下每个属性具有响应性
    return {
     
      ...refData,
      num,
      updateTitle,
      numFn,
    }
  }

– vue抽离逻辑模块(自定义hooks)

// src/hooks/useMousePosition.ts
import {
      ref, onMounted, onUnmounted } from 'vue'
function useMousePosition() {
     
  const x = ref(0)
  const y = ref(0)
  const updateMouse = (e: MouseEvent) => {
     
    x.value = e.pageX
    y.value = e.pageY
  }
  onMounted(() => {
     
    document.addEventListener('click', updateMouse)
  })
  onUnmounted(() => {
     
    document.removeEventListener('click', updateMouse)
  })
  return {
     x, y}
}

export default useMousePosition

// src/App.vue
import useMousePosition from './hooks/useMousePosition'
  setup() {
     
  
    const {
      x, y } = useMousePosition()
    return {
     
    }
  }

–发送请求

/src/hooks/useURLLoader
import {
      ref } from 'vue'
import axios from 'axios'

function useURLLoader<T>(url: string) {
     
  const result = ref<T | null>(null)
  const loading = ref(true)
  const loaded = ref(false)
  const error = ref(null)

  axios.get(url).then((rawData) => {
     
    loading.value = false
    loaded.value = true
    result.value = rawData.data
  }).catch(e => {
     
    error.value = e
    loading.value = false
  })
  return {
     
    result,
    loading,
    error,
    loaded
  }
}
export default useURLLoader

//src/App.vue
<h1 v-if="loading">Loading!...</h1>
<img v-if="loaded" :src="result.message" />
import useURLLoader from './hooks/useURLLoader'
setup() {
     

    const {
      result, loading, loaded } = useURLLoader(
      'https://dog.ceo/api/breeds/image/random'
    )
    return {
     
      result,
      loading,
      loaded,
    }
  }

– vue内置组件Suspense(展示请求后的组件)

/src/App.vue
    <Suspense> 
      <!-- Suspense包裹请求成功的组件  -->
      <template #default>
        <div>
          <async-show />
          <dog-show />
        </div>
      </template>
      <template #fallback>
        <h1>Loading !...</h1>
      </template>
    </Suspense>
import AsyncShow from './components/AsyncShow.vue'
import DogShow from './components/DogShow.vue'
 components:{
     
    AsyncShow,
    DogShow
  },
setup() {
     
    const error = ref(null)
    onErrorCaptured((e: any) => {
     
      // 抓取请求错误
      error.value = e
      alert(e)
      return true // 向上传播
    })
    return {
     
      error,
    }
  }

// src/components/AsyncShow
<template>
  <h1>{
     {
     result}}</h1>
</template>
<script lang="ts">
import {
      defineComponent } from 'vue'
export default defineComponent({
     
  setup() {
     
    return new Promise((resolve) => {
     
      setTimeout(() => {
     
        return resolve({
     
          result: 42
        })
      }, 3000)
    })
  }
})
</script>


// src/components/DogShow
<template>
  <img :src="result && result.message">
</template>
<script lang="ts">
import axios from 'axios'
import {
      defineComponent } from 'vue'
export default defineComponent({
     
  async setup() {
     
    const rawData = await axios.get('https://dog.ceo/api/breeds/image/random')
    return {
     
      result: rawData.data
    }
  }
})
</script>

–teleport (根组件展示)

src/components/Modal
<template>
<teleport to="#modal">
  <div id="center" v-if="isOpen">
    <h2><slot>this is a modal</slot></h2>
    <button @click="buttonClick">Close</button>
  </div>
</teleport>
</template>
<script lang="ts">
import {
      defineComponent } from 'vue'
export default defineComponent({
     
  props: {
     
    isOpen: Boolean,
  },
  // emits: {
     
  //   'close-modal': null // 可以添加验证事件名称,不验证是null
  // },
  emits: ["close-modal"],
  setup(props, context) {
     
    const buttonClick = () => {
     
      context.emit('close-modal')
    }
    return {
     
      buttonClick
    }
  }
})
</script>
<style>
  #center {
     
    width: 200px;
    height: 200px;
    border: 2px solid black;
    background: white;
    position: fixed;
    left: 50%;
    top: 50%;
    margin-left: -100px;
    margin-top: -100px;
  }
</style>

/src/App.vue
    <button @click="openModal">Open Modal</button><br />
    <modal :isOpen="modalIsOpen" @close-modal="onModalClose">
      My Modal !!!!</modal
    >
    import Modal from './components/Modal.vue'
  components: {
     
    Modal,
  },
  setup() {
     
    const modalIsOpen = ref(false)
    const openModal = () => {
     
      modalIsOpen.value = true
    }
    const onModalClose = () => {
     
      modalIsOpen.value = false
    }
    return {
     

      modalIsOpen,
      openModal,
      onModalClose,
    }
  }
// publice/index.html
    <div id="app"></div>
    <div id="modal"></div>

–vue全局配置
-全局api

Vue.config — app.config
config.productionTip被删除
config.ignoreElements 改名 config.isCustomElement
config.keyCodes被删除

-全局注册类

Vue.component - app.component
Vue.directive - app.directive

-行为拓展类api

Vue.mixin - app.mixin
Vue.use - app.use

-treeshaking api

//vue2
import Vue from 'vue'
Vue.nextTick(()=>{
     })
const obj = Vue.observable({
     })

//vue3
import Vue, {
      nextTick, observable } from 'vue'
Vue.nextTick // undefined
nextTick(()=>{
     })
const obj = observable({
     })

-setup中跳转

import {
      useRouter } from "vue-router";

  setup(){
     
    const router = useRouter();
    function login(){
     
        router.push("/home");
    }
  }

setup获取传递的参数
https://blog.csdn.net/weixin_40461134/article/details/108888438

你可能感兴趣的:(vue)