快速上手quasar-cli + vue3

2,241 阅读4分钟

前言

通过该分享,你将快速了解到以下知识点:

  1. thd3.0技术涵盖范围
  2. quasar-cli 全局配置简介
  3. Quasar 与 Element-ui 的横向对比
    • 文档对比
    • 组件对比
    • 实际代码对比
  4. vue3
    • vue3 解决了开发时的哪些痛点
    • vue3常见语法(响应式、setup语法,父子传值、事件监听、v-model语法等)
    • vue3 相较于 vue2 的不同点
    • pinia 解决了哪些问题

一、技术涵盖范围

知识必备

效率提升

打包构建

三方插件

二、quasar-cli 全局配置简介

  • layout等相关全局配置: scr目录下 config.ts

  • 全局样式配置:src目录下app.scss配置全局样式;quasar.variables.scss配置quasar的全局样式;根目录下的tailwind.config.js 文件可全局配置 tailwind 主题样式

  • 打包、代理、插件、样式引入配置:quasar.config.js

  • 三方插件引入: src目录下 boot 文件夹

  • 相关类型声明文件: src目录下 *.d.ts 如: env.d.ts、quasar.d.ts

  • 环境变量配置: 根目录下 .env文件

  • 埋点监控配置: 根目录下 index.html

三、Quasar 与 Element-ui 的横向对比

为了让大家可以更快的上手quasar这套ui框架,以及明白我们为什么选用quasar.下面会将大家熟悉的element-ui 和 quasar做横向对比。

  1. quasar的优势

  2. 文档阅读习惯,举例 q-layout; 对比 q-table vs el-table

  3. 组件案例展示 举例 q-card

四、vue3

vue3 解决了开发时的哪些痛点

1. 性能比Vue2快1.2~2倍: diff算法优化; 静态提升;事件侦听器缓存; ssr渲染

2. Vue3 按需编译,体积比Vue2更小: 重写了虚拟dom; 组合 API ; fragment(代码片段)

3. 支持组合API,类似React Hooks: 更好的逻辑复用;更灵活的代码组织;高内聚,低耦合 image.png 4. 更好的支持TS: 新增了 defineComponent 函数,使组件在 ts 下,更好的利用参数类型推断。如:reactive 和 ref 很具有代表性。

5. 提供了更先进的组件: Fragment;Teleport

6. proxy 相对 Object.defineProperty: proxy 可以直接监听对象,数组; 不需要初始化的时候遍历所有属性; 有 13 种拦截方法

更多...

vue3常见语法

1.组合式 API:setup()

基本用法

<script> 
import { ref } from 'vue' 
import PeoplePicker from './components/PeoplePicker.vue'; 

export default { 
    props: { title: String },
    components: {
        PeoplePicker
    },
    setup(props, context) { 
        const count = ref(0) // 返回值会暴露给模板和其他的选项式 API 钩子
        
        // 获取props传来的值
        console.log(props.title)
        
        // 透传 Attributes(非响应式的对象,等价于 $attrs)
        console.log(context.attrs) 
        
        // 插槽(非响应式的对象,等价于 $slots)      
        console.log(context.slots) 
        
        // 触发事件(函数,等价于 $emit) 
        console.log(context.emit) 
        
        // 暴露公共属性(函数) 
        console.log(context.expose)
        
        return { count } 
    }
}, 
</script>

使用setup语法糖后

  • 不用写setup函数;
  • 组件只需要引入不需要注册;
  • 属性和方法也不需要再返回,可以直接在template模板中使用

setup语法糖中新增的api

    1. defineProps:子组件接收父组件中传来的props
    1. defineEmits:子组件调用父组件中的方法
    1. defineExpose:子组件暴露属性,可以在父组件中拿到
<script setup> 
import { ref } from 'vue' 
import PeoplePicker from './components/PeoplePicker.vue'; 

export default { 
    const props = defineProps({ title: String }) 
    const emit = defineEmits(['change', 'delete'])

    const count = ref(0) // 返回值会暴露给模板和其他的选项式 API 钩子

    // 获取props传来的值
    console.log(props.title)
 
}, 
</script>

2. 响应式

举例 ref

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">
    {{ count }} <!-- 无需 .value -->
  </button>
</template>

举例 reactive

<script setup>
import { reactive } from 'vue'

const state = reactive({ count: 0 })

function increment() {
  state.count++
}
</script>

<template>
  <button @click="increment">
    {{ state.count }}
  </button>
</template>

3. 类与样式绑定

我们可以给 :class (v-bind:class 的缩写) 传递一个对象来动态切换 class:

<div :class="classObject"></div>

const classObject = reactive({
  active: true,
  'text-danger': false
})

4. 事件监听

watch监听对象属性变化

import { watch, reactive } from 'vue';

const obj = reactive({ count: 0 })

watch(()=>obj.count, (newValue, oldValue) => {
  console.log('newValue', newValue)
  console.log('oldValue', oldValue)
})

obj.count++

watchEffect

watch() 是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调,watchEffect就可以满足该需求。

import { watch, reactive } from 'vue';

const obj = reactive({ count: 0 })

watchEffect(() => { 
   console.log('value', obj.count)
})

obj.count++

5. 父子传值

子组件通过 Props 接受父组件传来的值

parent.vue

<template>
    <Child title="Components Title" :isClickable="true" />
</template>

<script setup lang="ts">
    import Child from './components/Child/index.vue';
</script>

child.vue

<template>
    <h2 class="text-4xl text-left leading-8">
        {{ title }}
    </h2>
    <q-btn color="primary" label="buttom" :disabled="!isClickable"/>
</template>

<script setup lang="ts">
    interface Props {
      title: string
      isClickable?: boolean
    }

    withDefaults(defineProps<Props>(), {
        title: '',
        isClickable: false
    })
</script>

子组件通过 Emit 传值给父组件

child.vue

<template>
    <q-form
          @submit="onSubmit"
          class="q-gutter-md"
        >
          <q-input
            filled
            v-model="name"
            label="Your name *"
            hint="Name and surname"
            lazy-rules
            :rules="[ val => val && val.length > 0 || 'Please type something']"
          />

          <q-input
            filled
            type="number"
            v-model="age"
            label="Your age *"
            lazy-rules
            :rules="[
              val => val !== null && val !== '' || 'Please type your age',
              val => val > 0 && val < 100 || 'Please type a real age'
            ]"
          />
          <div>
            <q-btn label="Submit" type="submit" color="primary" :disabled="!isClickable"/>
          </div>
    </q-form>
</template>

<script setup lang="ts">
    import { ref } from 'vue';

    const name = ref('')
    const age = ref()

    const emits = defineEmits(['submitData'])

    function onSubmit(){
      const submitData = {
        name: name.value,
        age: age.value
      };
      emits('submitData', submitData)
    }
</script>

parent.vue

<template>
    <Child title="child Components" :isClickable="true" @submitData="getChildSubmitData" />
</template>

<script lang="ts" setup>
    import Child from './components/Child/index.vue';

    function getChildSubmitData (data){
       console.log(data);
    }
</script>

6. 组件中 v-model 用法

parent.vue

<CustomInput v-model="searchText" />

CustomInput.vue

<template>
  <input v-model="value" />
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const value = computed({
  get() {
    return props.modelValue
  },
  set(value) {
    emit('update:modelValue', value)
  }
})
</script>

Provide: 要为组件后代提供数据,需要使用到 provide() 函数

Inject: 要注入上层组件提供的数据,需使用 inject() 函数:

image.png

vue3 相较于 vue2 的不同点

  • this基本不使用
  • setUp的使用
  • vue3废除过滤器 filter,通过 methods 或 computed 的方案实现
  • vue3 组件模板没有唯一顶层元素的限制
  • Vue3 有 createApp(),而 Vue2 的是 new Vue()
  • watchEffect用来代替生命周期里的onMounted和onUpdated
  • slot具名插槽的使用
  • 生命周期对比
Vue2--------------vue3
beforeCreate  -> setup()
created       -> setup()
beforeMount   -> onBeforeMount
mounted       -> onMounted
beforeUpdate  -> onBeforeUpdate
updated       -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed     -> onUnmounted
activated     -> onActivated
deactivated   -> onDeactivated
errorCaptured -> onErrorCaptured
  • 在 Vue3 中,全局和内部 API 都经过了重构,并考虑到了 tree-shaking 的支持。因此,全局 API现在只能作为 ES 模块构建的命名导出进行访问。

pinia 解决了哪些问题

  • 解决多个组件共享状态时,单向数据流的简洁性很容易被破坏的问题
  • 组件之间的复杂通信,做状态统一集中管理
  • 解决多个视图依赖同一状态的问题
  • store 中的状态不能直接更改,唯一途径就是显式地提交

总结:存储响应式,易通信,易管理,易追踪

  • Pinia vs Vuex