Vue3 + Vite 学习

1 篇文章 0 订阅
1 篇文章 0 订阅

Vue3 + Vite 学习

Vue3官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)

1.Vue3基本介绍

① Vue3的优点

② Vue3 + Vite 项目创建

  npm create vue@latest

可选功能配置:

③ 项目目录与关键文件

2.组合式API

① setup选项 与 setup语法糖

a. setup 特征
  1. 执行时机比beforeCreate早

  2. setup 函数中 ,获取不到 this

  3. 数据和函数需要在 setup 中 return 才能在模板中使用

b. 选项式与语法糖对比
  # 原始复杂代码(选项式代码)
  <script>
  export default{
    setup(){
      // 数据
      const message = 'Hello Vue3'
      // 函数
      const logMessage = () => {
        console.log(message)
      }
      return{
        message,
        logMessage
      }
    }
  }
  </script>
  ​
  # setup 语法糖代码
  <script setup>
  // 数据
  const message = 'this is a message'
  // 函数
  const logMessage = () => {
    console.log(message);
  }
  </script>

② reactive 和 ref 函数

a. reactive 函数

作用:接收 对象类型数据的参数 传入并返回一个 响应式的对象

  
  <script setup>
  // 导入
  import { reactive } from 'vue'
  // 执行函数 传入参数 变量接收
  const state = reactive({ count:100 })
  const setCount = () => {
    state.count++
  }
  </script>
  ​
  <template>
    <div>
      {{ state.count }}
    </div>
    <button @click="setCount">+1</button>
  </template>
b. ref 函数 ( 推荐使用 )

作用:接收 简单类型或者对象类型的数据 传入并返回一个 响应式的对象

注意点:1. 在 script 中访问数据,需要通过 .value

​ 2. 在 template 中, 不需要加 .value

  
  <script setup>
  // 导入
  import { ref } from 'vue'
  // 执行函数 传入参数 变量接收
  const count = ref(0)
  const setCount = () => {
      // 获取 ref 对象中的数据
      count.value++
  }
  </script>
  ​
  <template>
    <div>{{ count }}</div>
    <button @click="setCount">+1</button>
  </template>
c. reactive VS ref
  1. reactive 不能处理简单类型的数据

  2. ref 参数类型支持更好但是必须通过 .value 访问修改

  3. ref 函数的内部实现依赖于 reactive 函数

③ computed函数

核心步骤:

1.导入 computed 函数

2.执行函数在回调参数中 return 基于响应式数据做计算的值,用变量接收

  
  const 计算属性 = computed(() => {
     return 计算返回的结果
  })
  ​
  <script setup>
  // 导入
  import { computed, ref } from 'vue';
  ​
  // 声明数据
  const list = ref([1,2,3,4,5,6,7,8])
  // 计算属性:基于 list 派生一个计算属性,从 list 中过滤出 > 2 的数
  const computedList = computed(() => {
    return list.value.filter(item => item > 3)
  })
  ​
  // 定义一个修改数组的方法
  const addfn = () => {
    list.value.push(666)
  }
  </script>
  ​
  <template>
    <div>
      <div>计算前列表:{{ list }}</div>
      <div>计算后列表:{{ computedList }}</div>
      <button @click="addfn" type="button">修改数组</button>
    </div>
  </template>

注意:1. 计算属性中不应该出现"副作用":如异步请求/修改dom;

​ 2. 避免直接修改计算属性的值。

④ watch函数

作用:侦听一个或者多个数据的变化,数据变化时执行回调函数

两个额外参数:

immediate ( 立即执行 ) :在侦听器创建时立刻触发回调,响应式数据变化后继续执行回调

deep ( 深度侦听 ) :当ref(复杂类型) 时,需开启 deep 监视复杂类型内部数据的变化

  
  <script setup>
  // 导入
  import { ref, watch } from 'vue';
  // 声明数据( 对象 )
  const count = ref(0)
  const nickname = ref('张三')
  const userInfo = ref({
    name: 'zs',
    age: 18
  })
  //定义方法
  const changeCount = () => {
    count.value++
  }
  const changeNickname = () => {
    nickname.value = '李四'
  }
  const setUserInfo = () => {
    userInfo.value.age++
    userInfo.value.name = 'ls'
  }
  ​
  // 1. 监听单个数据的变化
    // watch(ref对象,(newValue, oldValue) => { ... })
  watch(count, (newCount, oldCount) => {
    console.log(newCount, oldCount);
  })
  // 2.监听多个数据的变化
    // watch([ref对象1,ref对象2], (newArr,oldArr) => { ... })
  watch([count, nickname], (newArr, oldArr) => {
    console.log(newArr, oldArr);
  })
  // 3.immediate 立刻执行 
  watch(count, (newCount, oldCount) => {
    console.log(newCount, oldCount);
  },{
    immediate: true
  })
  // 4.deep 深度监视,默认 watch 进行的是 浅层监视
  //    const ref1 = ref(简单类型) 可以直接监视
  //    const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化
  watch(userInfo,(newValue) => {
    console.log(newValue);
  },{
    deep: true
  })
  // 5. 在不开启deep的前提下,精确侦听对象的某个属性
  watch(() => userInfo.value.age, (newValue, oldValue) => {
    console.log(newValue, oldValue);
  })
  </script>
  ​
  <template>
    <div>{{ count }}</div>
    <button @click="changeCount">修改数字</button>
    <div>{{ nickname }}</div>
    <button @click="changeNickname">修改昵称</button>
    <div>-----------------------</div>
    <div>{{ userInfo }}</div>
    <button @click="setUserInfo">修改userInfo</button>
  </template>

⑤ 生命周期函数

    <script setup>
  import { onMounted } from "vue"
  // beforeCreate 和 create 的相关代码
  // 一律放在 setup 中执行
  const getList = () => {
    setTimeout(() => {
      console.log('发送请求,获取数据')
    },2000)
  }
  // 一进入页面的请求
  getList()
  ​
  // mounted生命周期中执行
  onMounted(() => {
    console.log('mounted生命周期函数-1')
  })
  // 写成函数的调用方式可多次调用,并不会冲突,而是按照顺序以此执行
  onMounted(() => {
    console.log('mounted生命周期函数-2')
  })
  </script>

⑥ 父子通信

a. 父传子

基本思想:1. 父组件中给 子组件绑定属性

​ 2. 子组件内部通过 props 选项接收

  
  // 父组件
  <script setup>
  import { ref } from 'vue'
  // 引入子组件
  import SonCom from "./components/son-com.vue"
  const num = ref(99)
  const numAdd = () => {
      num.value += 10
  }
  </script>
  ​
  <template>
    <div>
      <h3>
          父组件 - {{ num }}
          <button @click = "numAdd"> + </button>
      </h3>
      <!-- 1. 绑定属性 message 动态传递 num -->
      <!-- 给子组件,添加属性的方式传值 -->  
      <SonCom message="this is a message" :num=num></SonCom>
    </div>
  </template>
  -------------------------------------------------------------------------
  // 子组件
  <script setup>
  // 2. 通过 defineProps "编译器宏" 接收子组件传递的数据
  const props = defineProps({
    message: String,
    num: Number
  })
    console.log(props.message)
    console.log(props.num)
  </script>
  ​
  <template>
    <div class="son">
      <!-- 对于 props 传递过来的数据,模板中可以直接使用 -->
      子组件 - {{ message }} - {{ num }}
    </div>
  </template>
b. 子传父

基本思想:1. 父组件中给 子组件标签通过 @ 绑定事件

​ 2. 子组件内部通过 emit 方法触发事件

  
  // 父组件
  <script setup>
  import { ref } from 'vue'
  // 引入子组件
  import SonCom from "./components/son-com.vue"
  const num = ref(99)
  // 3. 触发自定义事件,并传递参数
  const getNum = (newNum) => {
      num.value = newNum
  }
  </script>
  ​
  <template>
    <div>
      <h3>父组件 - {{ num }}</h3>
      <!-- 1. 通过 @ 绑定自定义事件 -->
      <SonCom @get_num = getNum :num=num></SonCom>
    </div>
  </template>
  -------------------------------------------------------------------------
  // 子组件
  <script setup>
  const props = defineProps({
    num: Number
  })
  // 2. 通过 defineEmits 编译器宏生成 emit 方法
  const emit = defineEmits(['get_num'])
  const changeNum = () => {
      emit('get_num',66)
  }
  </script>
  ​
  <template>
    <div class="son">
      <!-- 对于 props 传递过来的数据,模板中可以直接使用 -->
      子组件 - {{ num }}
    <button @click="changeNum"></button>
    </div>
  </template>

⑦ 模板引用

概念:通过 ref标识 获取真实的 dom对象或者组件实例对象

模板引用步骤与注意事项:

  1. 调用 ref 函数,生成一个 ref对象 const 对象名 = ref(对象). 通过 ref 标识,进行绑定 ref = "对象名"

  2. 通过 ref对象.value 即可访问绑定的数据(必须渲染完成以后)

  3. 在默认情况下<script setup>语法糖下 组件内部的属性和方法时不开放给父组件访问的可以通过defineExpose 编译宏指定哪些属性和方法允许访问

  
  <script setup>
  import TestCom from "./components/test-com.vue"
  import { onMounted, ref } from 'vue'
  // 获取 dom
  const inp = ref(null)
  // 1.进入页面就聚焦
  onMounted(() => {
    // console.log(inp.value)
    // inp.value.focus()
  })
  // 2.点击按钮聚焦
  const clickFn = () => {
    inp.value.focus()
  }
  // 获取 组件
  const testref = ref(null)
  const getCom = () => {
    console.log(testref.value);
  }
  </script>
  ​
  <template>
    <div>
      <input ref="inp" type="text">
      <button @click="clickFn">获取焦点</button>
    </div>
      <TestCom ref="testref"></TestCom>
      <button @click="getCom">获取组件</button>
  </template>
  --------------------------------------------
  [test-com.vue]
  <script setup>
  const count = 999
  const sayHi = () => {
    console.log("打招呼");
  }
  defineExpose({
    count,
    sayHi
  })
  </script>
  ​
  <template>
    <div>    测试组件 - {{ count }}  </div>
  </template>

⑧ provide 和 inject

作用:顶层组件向任意的底层组件 传递数据和方法,实现 跨层组件通信

跨层传递数据:

  1. 顶层组件通过 provide函数提供 数据

  2. 底层组件通过 inject函数获取 数据

  
  [top-com.vue]
  <script setup>
  import { provide, ref } from 'vue'
  import MiddleCom from './components/middle-com.vue'
  ​
  // 1. 跨层传递普通数据
  provide('theme-color','blue')
  ​
  // 2. 跨层传递响应式数据
  const count = ref(100)
  provide('count',count)
  setTimeout(() => {
    count.value = 500
  },2000)
  ​
  // 3. 跨层传递函数
  provide('changeCount', (newCount) => {
    count.value = newCount
  })
  </script>
  ​
  <template>
    <div>
      <h1>顶层组件</h1>
      <MiddleCom></MiddleCom>
    </div>
  </template>
  ----------------------------------------------------------
  [middle-com.vue]
  <script setup>
  import BottomCom from './bottom-com.vue'
  </script>
  ​
  <template>
    <div>
      <h2>中间层组件</h2>
      <BottomCom></BottomCom>
    </div>
  </template>
  ----------------------------------------------------------
  [bottom-com.vue]
  <script setup>
  import { inject } from 'vue'
  ​
  const themeColor = inject('theme-color')
  const count = inject('count')
  const changeCount = inject('changeCount')
  const clickFn = () =>{
    changeCount(1000)
  }
  </script>
  ​
  <template>
    <div>
      <h3>底层组件-{{ themeColor }}-{{ count }}</h3>
      <button @click="clickFn">更新count</button>
    </div>
  </template>

3. 新特性

① defineOptions

作用:用于定义 Options API 的选项,除 props,emits,expose,slots外。

  
  <script setup>
  defineOptions({
      name:'NameIndex'
  })
  </script>
  ​
  <template>
    <div>
    defineOptions
    </div>
  </template>

② defineModel

  
  <script setup>
  import { ref } from 'vue'
  import MyInput from './components/my-input.vue'
  ​
  const txt = ref('123456')
  </script>
  ​
  <template>
    <div>
      <MyInput v-model="txt"></MyInput>
      {{ txt }}
    </div>
  </template>
  -----------------------------------------------
  <script setup>
  import { defineModel } from 'vue'
  const modelValue = defineModel()
  </script>
  ​
  <template>
    <div>
      <input 
        type="text" 
        :value="modelValue" 
        @input="e => modelValue = e.target.value"
      >
    </div>
  </template>
  --------------------------------------------
  [vite.config.js]
  export default defineConfig({
    plugins: [
      vue({
        script:{
          defineModel:true
        }
      }),
    ]

4. Pinia

① 安装与配置

  
  npm install pinia  

  [main.js]
  import { createApp } from 'vue'
  import { createPinia } from 'pinia'
  import App from './App.vue'
  ​
  const pinia = createPinia()
  const app = createApp(App)
  ​
  // 注意 pinia 与 vue 的版本匹配,否则会出现报错:
  // 与类型“Pinia”和“Plugin<[]>”相比,堆栈深度过高。ts(2321)
  app.use(pinia)
  app.mount('#app')

② Store

  
  [src/store/.js]
  import { defineStore } from "pinia"
  import axios from "axios"
  import { computed, ref } from "vue"
  ​
  // 定义 Store
  //  defineStore(仓库的唯一标识,() => {...})
  export const usePiniaStore = defineStore('Pinia',() => {
  ​
  // 声明数据 => state
  const count = ref(100)
  const msg = ref('pinia')
  const channelList = ref([])
  ​
  // 声明操作数据的方法 => action (普通函数)
  const addCount = () => count.value++
  const subCount = () => count.value--
  // 支持异步获取数据
  const getList = async () => {
  const { data: { data }} = await axios.get('http://geek.itheima.net/v1_0/channels')
  channelList.value = data.channels
  console.log(data.channels)
  }
  ​
  // 声明基于数据派生的计算属性 => getters (computed)
  const double = computed(() => count.value * 2)
  const half = computed(() => count.value / 2)
  ​
  // 声明 getters 相关 暴露数据、方法和计算属性,组件可直接使用
  return {
  count,
  msg,
  addCount,
  subCount,
  double,
  half,
  channelList,
  getList
  }
  })
  -------------------------------------------
  [component/.vue]
  <script setup>
  <!-- 导入并调用 --!>
  import { storeToRefs } from 'pinia'
  import { usePiniaStore } from './store/piniaTest'
  const piniaStore = usePiniaStore()
  ​
  // 若直接解构不处理,数据会丢失响应式,需要添加 storeToRefs
  // 但解构方法不需要添加
  const { count, msg } = storeToRefs(piniaStore)
  const { addCount } = piniaStore
  </script>
  ​
  <template>
    <div>Pinia -
      {{ msg }} -
      {{ count }} - 
      {{ piniaStore.double }} -
      {{ piniaStore.half }}
    </div>
    <div>
      <button @click="piniaStore.subCount"> - </button>
      - {{ piniaStore.count }} - 
      <button @click="addCount"> + </button>
      <hr>
      <button @click="piniaStore.getList">获取数据</button>
      <ul>
        <li v-for="item in piniaStore.channelList" :key="item.id">{{ item.name }}</li>
      </ul>
    </div>
  </template>

③ Pinia持久化插件

  
  安装:
  npm i pinia-plugin-persistedstate -D
  ​
  // 使用/模块内
  export const useStore = defineStore('use-store', () => {
    const ...
    return {
        ...
    }
  },
  {
      persist:true
  }
  )

④ Pinia 配置仓库统一管理

  
  // stores/index.js
  import { createPinia } from 'pinia'
  import persist from 'pinia-plugin-persistedstate'
  ​
  const pinia = createPinia()
  pinia.use(persist)
  export default pinia
  ​
  // 统一导出
  export * from './modules/user'
  export * from './modules/counter'
  ​
  // main.js配置
  import pinia from '../src/stores/index'
  app.use(pinia)
  ​
  // vue中调用方式:
  <script setup>
  improt {useUserStore, useCountStore} from '../src/stores'
  const userStore = useUserStore() 
  const countStore = useCountStore()
  </script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值