vite创建vue3项目

237 阅读4分钟

1.创建项目

根据vite官网,npm命令行创建项目,并且可以指定项目名称和想要使用的模板,这里我们使用vue

    npm init @vitejs/app myvite --template vue

项目创建完成后,我们发现项目结构有与vue-cli创建的项目有稍许差别,而且没有vue-router等依赖的安装,创建项目速度炒鸡快,当然这也就说明我们需要自己引入vue-router等依赖。

根据官网的指引我们找到vue-router官网:router.

安装

    npm install vue-router@4

创建router文件:

// 引入
import { createRouter, createWebHashHistory } from "vue-router";

// 创建
const router = createRouter({
    history: createWebHashHistory(),
    routes:[
        //这里我们使用了别名@,需要再vite.config.js配置
        /***
        * import path from 'path'
        * resolve: {
        *    alias: {
        *     '@': path.resolve(__dirname, 'src'),
        *    }
        * },
        */
        { path: '/', component: ()=> import('@/components/home.vue') },
    ]
})
export default router;

安装vuex

    npm install vuex@next --save

接下来我们简单实现一下移动端切换路由滑动效果:

//route.js
routes: [
        {
            path: '/',
            name: '首页',
            meta: {
               //后面会用到这个参数进行判断  路由深度决定向左向右移动
                depth: 1
            },
            component: () => import('@/components/home.vue')
        },
        {
            path: '/list',
            name: '列表页',
            meta: {
                depth: 2
            },
            component: () => import('@/components/list.vue')
        },
        {
            path: '/detail',
            name: '详情页',
            meta: {
                depth: 3
            },
            component: () => import('@/components/detail.vue')
        },
]

我们需要再app.vue的router-view中进行过渡动画操作

//app.vue
//HTML
//transitionName  用来设置过渡样式名称
<transition :name="transitionName">
   <router-view></router-view>
</transition>

//js
<script>
import { useRoute, onBeforeRouteUpdate } from 'vue-router'
import { watch, ref } from "vue";
export default {
  setup() {
    //onBeforeRouteUpdate((to,from) =>{
    //  console.log(to,from)
    //})
    let transitionName = ref('')
    const route = useRoute()
    watch(
       //监听路由的meta
      () => route.meta,
      (to, from) => {
        // console.log(to, from)
        if (from.depth) {
          //根据meta深度修改样式名称
          transitionName.value = to.depth > from.depth ? 'slide_left' : 'slide_right';
        }
      }
    )
    return {
      transitionName
    }
  } 
}
//[过渡动画](https://v3.cn.vuejs.org/guide/transitions-enterleave.html#%E5%8D%95%E5%85%83%E7%B4%A0-%E7%BB%84%E4%BB%B6%E7%9A%84%E8%BF%87%E6%B8%A1)
//CSS
.slide_left-enter-active,
.slide_left-leave-active,
.slide_right-enter-active,
.slide_right-leave-active {
  will-change: transform;   //告知浏览器该元素会有哪些变化的方法
  transition: all 350ms;
  position: absolute;
  overflow: hidden;
}
.slide_right-enter-from,
.slide_left-leave-active {
  transform: translate(-100%, 0);
}
.slide_left-enter-from,
.slide_right-leave-active {
  transform: translate(100%, 0);
}
</script>

创建vuex文件

安装

    npm install vuex@next --save  
import { createStore } from "vuex";
import home from "@/store/modules/home";
const store = createStore({
    state(){
        return{

        }
    },
    mutations:{

    },
    modules:{
        ...home
    }
})
export default store;

vuex数据管理

const state = {
   name:'' 
}
const mutations = { }
const actions = { }
export default{
    state,
    mutations,
    actions
}
//home.vue
<div>{{name}}</div>

import { useStore } from "vuex";
setup(){
  const store = useStore()
  return{ 
    name: computed(() => store.state.name),
  }
}

computed的使用

计算属性可以在我们开发工作中处理数据成为我们的一大臂力。我们可以先了解下它如何使用:

    //html
    <div class='computed'>
       <input type="text" v-model="inputVal">
       <input type="text" v-model="computedVal">
    </div>
    // js
    import { ref, computed } from 'vue';
    setup(props) {
      const computedVal = computed({
        get(){
           //处理数据
            return `用户名:${inputVal.value}`;
        },
        set(val){
            console.log(val);
        }
     })
    return{
       inputVal,
       computedVal  
    }

异步组件defineAsyncComponent

创建一个只有在需要时才会加载的异步组件。

基础用法:

//defineAsyncComponent.vue
<div class="defineAsyncComponent">
    <a-table :dataSource="dataSource" :columns="columns" />
</div>

 async setup(props) {
    await new Promise(resolve=>{
        setTimeout(() => {  //这里用settiemout来模拟ajax操作
            resolve()
        }, 2000);
    })
    return {}
}
//father.vue
 <suspense> //后面suspense可能会进行修改,是一个试验性的新特性
        //当default可以显示时显示default
    <template #default>  
        <asyncTable></asyncTable>
    </template>
    //当default不可显示时候显示fallback
    <template #fallback>
        loading...
    </template>
 </suspense>
 
 import { defineAsyncComponent, h } from 'vue'
 
 components: {
        // AsyncComp
   asyncTable: defineAsyncComponent(() => import('@/components/vueApi/defineAsyncComponent.vue')),
 }

defineAsyncComponent的高阶用法我这边显示是有问题的,哪位大佬如果有用过希望可以不吝赐教一下

Teleport的使用

当我们想要创建一个包含在某个组件中但是又想让它全屏显示的时候,可以用到teleport,他可以控制我们在哪个节点下渲染html:

    <teleport to='body'>
        <div style='width:100%;height:100%;'>
            <!-- 内部dom节点 -->
        </div>
    </teleport>

上面例子我们将div控制输出在body元素下面,这样就不用css去设置固定位置了。

$attrs的使用

$attrs 现在包含了所有传递给组件的 attribute,包括 class 和 style。 又或者父组件下面有多根节点子组件,这个时候就可以个指定的自杜建传递attrs了。

    //father.vue
    <detail style="color:red"></detail>
    
    //detail.vue
    //当只有一个父节点时候
    <div> 
        <div class="detail">
            age    //color:red
        </div>
        <div v-bind="$attrs">
            name   //color:red
        </div>
    </div>
    
    //当根元素有两个节点时候
    <div class="detail">
        age  
    </div>
    //绑定$attrs的样式是继承的
    <div v-bind="$attrs"> 
        name   //color:red
    </div>

h() 函数的应用

//html
<functionaldom message='巧了'></functionaldom>

//js
const functionaldom = (props,context) => {
    console.log(props,context);
    return h('div',
        context.attrs,
        h('span', {
            onClick: ()=> console.log(props.message),
            style:{
                //驼峰
            }
        },props.message
        )
    )
}

components: {
    functionaldom
}

vue3中一些更改

  • 同一个元素上面的v-ifv-for的优先级问题。v-ifv-for优先级更高,但是尽量不要同时使用。

  • vue不提供createElement方法(通常为h()),直接从依赖中导出。

  • 当标签的属性和绑定对象的属性名称相同时,以排在后面的为准

vue3 具有颠覆意义的响应-组合api

  • setup函数 组件创建前执行的初始化函数,默认参数包含 propscontextcontext 可以理解为组件实例挂载之前的上下文(虽然这么理解,但不是实例本身,这点要清楚),所以实例上的 attrsslotsemit 可以在 context 上访问到。
  • reactive | readonly | shallowReactive | shallowReadonly 用于创建响应式的对象,readonly将创建只读对象不可进行修改,isReactive 可以判断对象是否为响应式的。isProxy 区分是哪种方法创建的代理对象,isReadonly 判断对象是否为 readonly 创建。shallowXXX 根据名称可知,只将对象自身的浅层属性进行转换,深层属性保持不变。
//html
    {{something}} //setuo
//js
    setup(props) {
        const setsomething = reactive({name:'setup'})
        return {
            setsomething
        }
    },
  • toRaw() | markRaw() 前者返回代理对象的源对象,后者将普通对象转换为不可代理的对象。
    const setsomething = reactive({name:'setup'})
    console.log(setsomething)
    const torawvalue = toRaw(setsomething)
    console.log(torawvalue);

image.png

由上图可见 toRaw函数可以直接返回源对象。

  • ref | toRef | unref | toRefs | isRef | shallowRef | triggerRef

常用于包装基本类型值为响应式对象,例如 const box = ref(0), box.value === 0

unref: isRef(data) ? data.value : data //返回一个基本类型的的值

shallowRef:创建浅层的包装对象

triggerRef:人为触发订阅了 shallowRef 的副作用。

    // 声明一个基本类型的响应值
    const count = ref(0)
    //使用toRefs将不是响应式的值包裹成响应式的
    const {name} = toRefs(setsomething)

    // 基本类型的值一定要加.value,因为声明的是个对象
    const changeName = ()=>{
        setsomething.name = 'setup1100'
        count.value++
    }
  • effect | watch | watchEffect 对数据源的更新订阅,一旦订阅的数据发生变化,将自动执行副作用函数。效果与 vue2的 watch 配置类似。

effect:监听响应式对象的属性一旦发生变化就触发回调函数。

    effect(()=>{
        console.log(count.value)
    })
    /***
    *  watch第一个参数可以是响应式对象 count
    *  函数的返回值  ()=>count.value
    *  数组监听多个对象是否改变 [count,...]
    */
    watch([count,setsomething],(val)=>{
        console.log(val)
    })
  • computed
    计算属性 用法同vue2,可以在模板中引入使用

  • provide,inject

跨组件传递数据的方案:所有嵌套子组件可以在顶级父组件中拿到数据

    //father.vue
    provide('setupCount',count.value)
    
    //son.vue
    let getval = inject('setupCount')
    return{
       getval
    }

基于setup 方法使用的生命周期钩子

image.png

vue hooks

强大的代码复用能力 hook

//hook.js
import { ref, onMounted, watch, reactive } from 'vue'

const fetch = (id) => new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve([
      { name: 'user-' + Math.random(), id: 1 },
      { name: 'user-' + Math.random(), id: 2 },
      { name: 'user-' + Math.random(), id: 3 },
      { name: 'user-' + Math.random(), id: 4 },
      { name: 'user-' + Math.random(), id: 5 },
    ]);
  }, 200)
})

export default function useUserInfo(id) { // id 为响应式数据源
  const info = reactive({value: []})
  const loading = ref(false)

  const getUserInfo = () => {
    loading.value = true
    fetch(id.value).then(user => {
      info.value = user
      loading.value = false
    })
  }
  onMounted(getUserInfo)
  //监听id,进行数据修改
  watch(() => id.value, getUserInfo);
  
  return { info, loading }
}
//customHook.vue
<template>
  customHook: {{loading ? '下载中' : '下载完成'}}<button @click="refresh">重试</button>
  <ul>
    <li v-for="{name, id} in info.value" :key="id">{{name}}</li>
  </ul>
</template>

<script>
  import useUserInfo from './hooks'
  import { ref, useSlots } from 'vue'
  export default {
    setup(props, { slots }) {
      const id = ref(0);
      const { info, loading } = useUserInfo(id);
      //响应式数据id改变,触发上面修改数据方法
      const refresh = () => {
        id.value++;
      }
      
      return { info, loading, refresh }
    }
  }
</script>

hook的强大之处在于,当我们有很多相同的业务需要复用的时候,hook是很好的选择。

vue3的基本用法和更新地方大概就这些,后面开始项目创建。