vite+vue3学习记录

1

title: vite+vue3学习记录
permalink: /vue3
date: 2022-11-04 20:45:54
tags:

  • 技术
  • 习惯
  • 软件
    author: 林
    location: 广州

    summary: vite+vue3学习记录

准备工作

  • 创建项目 pnpm create vite

    √ Project name: … vite-vue3-demo //项目名称

    √ Select a framework: » Vue //前端框架

    √ Select a variant: » JavaScript

  • 运行

    pnpm i

    pnpm run dev

  • 安装依赖

    pnpm install vue-router@4
    pnpm install ant-design-vue
    pnpm install axios
    pnpm install sass --save-dev
    pnpm install @ant-design/icons-vue
    

响应式

官方api

reactive

创建一个响应式对象,仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。

示例

// javascript
import { reactive } from 'vue';
//定义响应式数据
const state = reactive({ count: 0,data: [5, 6, 7, 8]})
//1、 响应式
state.count++
//2、解构后无法保持响应式
let { count } = state
//无法保持响应式
count++;

//3、可以追加属性
state.newProp = { 'msg': '你好' }

//4、属性可替换赋值
state.data = { 'msg': '我变了' }

//5、【错】不能整个重新赋值 
// state={ count: 100 }
<p> {{ state }}</p>

ref

创建任意类型的响应式数据

示例

import { ref } from 'vue'
//定义响应式数据
const count = ref(0)
const objectRef = ref({ count: 0 })
//1、响应式访问需要.value
count.value++;
objectRef.value.count++;
//2、可追加属性
objectRef.value.data=[1,2,3,4]
//3、可替换赋值并保持响应式
objectRef.value={ msg: '惊呆了、我整个被替换了' }
<!-- 模板里不需要.value-->
<p>{{count}}</p>
<p>{{objectRef.count}}</p>

$ref 响应式语法糖

官方api
结合了ref和reactive的优点,该功能处于实验性阶段
需要在vite.config.js里配置reactivityTransform: true

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path, { resolve } from 'path'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue({ 
      reactivityTransform: true//响应式语法糖
     })
  ],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src')
    }
  },
})

示例

//1、响应式 基础类型 和对象类型都不需要.value
num++;
$refObj.count++;
//2、可以追加属性
$refObj.newProp = { 'msg': '你好' }
//3、可替换赋值并保持响应式
$refObj = { msg: '惊呆了、我整个被替换了' }
```html
<p> $refnum1 -->{{ num }}</p>
<p> $refObj -->{{ $refObj }}</p>

props

官方api

  • 父组件
    <template>
    <div>
        <h2>prop传参 示例</h2>
        <button @click="change">change</button>
        <p>num--><input v-model="num" type="number" /> </p> 
        <p>str-->{{ str }}</p> 
        <p>arr-->{{ arr }}</p> 
        <p>obj-->{{ obj }}</p> 
        <hr>
        <propsChild :arr="arr" :num="num" :str="str" :obj="obj" ></propsChild>
    </div>
    </template>
    

* 子组件

<div style="background-color: aqua;">
    <h2>子组件</h2> 
    <button @click="change">change</button>
    <p>num-->{{ num }}</p> 
    <p>str-->{{ str }}</p> 
    <p>arr-->{{ arr }}</p> 
    <p>obj-->{{ obj }}</p> 
</div>

>当对象或数组作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递,而对 Vue 来说,禁止这样的改动虽然可能,但有很大的性能损耗,比较得不偿失。
这种更改的主要缺陷是它允许了子组件以某种不明显的方式影响父组件的状态,可能会使数据流在将来变得更难以理解。在最佳实践中,你应该尽可能避免这样的更改,除非父子组件在设计上本来就需要紧密耦合。在大多数场景下,子组件应该抛出一个事件来通知父组件做出改变。

## emit 
[官方api](https://cn.vuejs.org/guide/components/events.html)

父子通信的一种方式,事件触发
* 示例

> 子组件 emitChild.vue

<div style="background-color:aqua;">
    <button @click="click1"> emit</button>
    <input :value="props.modelValue" @input="emit('update:modelValue', $event.target.value)" />
    <input :value="props.age" type="number" @input="emit('update:age', $event.target.value)" />
</div>


> 父组件

<div>
    <h2>emit</h2>
    <button @click="getEmitChild">获取子组件暴露属性方法</button>
    <input type="text" v-model="name">
    <input v-model="age" type="number" /> 
    <p>{{ loginDate }}</p>
    <!-- 事件回调@事件名 -->
    <emitChild ref="emitChild1" v-model="name" v-model:age="age" @change-parent="setLoginDate"></emitChild>
</div>

## slot
[官方api](https://cn.vuejs.org/guide/components/slots.html)
* 没有name的插槽为默认插槽,默认name为default
* 可用v-slot:slot1 也可用 #slot1
>定义

<!-- 默认内容 -->
<p>具名插槽1 </p>

>使用

<p> 覆盖具名1插槽</p>

* 示例

> 子组件

<p>组件1</p>
 <!-- 默认插槽  -->
<slot> 
    <button @click="click1" >默认插槽</button>
</slot>
<slot name="slot1">
    <!-- 默认内容 -->
    <p>具名插槽1 </p>
</slot>
<slot name="slot2"> 
</slot>


> 父组件

<div>
    <slotChild> 
        <template #default>
            <p> 覆盖默认插槽</p>
        </template>
        <!-- 可用v-slot:slot1 也可用 #slot1 -->
        <template #slot1>
            <p> 覆盖具名1插槽</p>
        </template>
        <template #slot2>
            <p> 覆盖具名2插槽</p>
        </template>
    </slotChild>
</div>

## 数据注入

[官方api](https://cn.vuejs.org/guide/components/provide-inject.html)

父组件向后代组件传递数据
* 一般>=3级使用,两级可以使用`props`
* `$ref`语法糖定义的数据在后代组件修改数据后貌似无法响应式
>父组件提供数据

>后代组件注入使用

import { inject } from 'vue'
const message = inject('注入名',默认值)

>示例
* 父组件

str1-><input v-model="str1" type="text" />
str2-><input v-model="str2" type="text" />
obj.count-><input v-model="obj.count" type="text" />
<hr>
<inject2></inject2>


* 后代组件

<div>
    <p>inject3</p>
    <p>obj-->{{obj}}</p>
    <p>str1--><input type="text" v-model="str1"></p>
    <!-- $ref 定义的基础数据注入后没办法响应式? -->
    <p>str2--><input type="text" v-model="str2"></p>
    <p>obj.count--><input type="text" v-model="obj.count"></p>
    <button @click="change"> change</button>
</div>

## 计算属性

[官方api](https://cn.vuejs.org/guide/essentials/computed.html)

>示例

<div>
    <input type="text" v-model="firstName">
    <input type="text" v-model="lastName">
    <p>fullName--{{ fullName }}</p>
</div>


## watch
[官方api](https://cn.vuejs.org/guide/essentials/watchers.html)
监听属性改变

watch(prop,callBack, {
immediate: Boolean, //在侦听器创建时立即触发回调
deep: Boolean //深度监听
flush:Boolean //调整回调的刷新时机'pre'(dom渲染之前) | 'post'(dom渲染之后) | 'sync'
})

>示例

<div>
    <button @click="change">change</button>
    <button @click="change2">change2</button>
    <input type="text" v-model="obj.d.stu.age">
</div>


## 生命周期

[官方api](https://cn.vuejs.org/guide/essentials/lifecycle.html)
[官方api](https://cn.vuejs.org/api/composition-api-lifecycle.html)

<div>
    <!-- 触发onUpdated -->
    <input type="text" v-model="name">
</div>


## iconfont
使用[阿里巴巴矢量图标库](https://www.iconfont.cn/) 创建简单的图标组件
* 1、资源管理-我的项目-生成symol 链接
* 2、在index.html引入 (可以将js下载下来)
<script src="//at.alicdn.com/t/c/font_3760320_azguqahs9pt.js"></script>
* 3、 编写icon-font 组件

<svg  :width="props.size" :height="props.size" :style="style" :class="`icon-font  ${props.class}`">
    <use :xlink:href="'#' + props.name"></use>
</svg>



* 4、在main.js里全局注册组件

import IconFont from “@/components/iconFont.vue”;
const app=createApp(App)
app.component(“IconFont”,IconFont);

* 5、使用组件

## hooks

[官方api](https://cn.vuejs.org/guide/reusability/composables.html#conventions-and-best-practices)[开源vueuse库](https://vueuse.org/)

逻辑复用、逻辑和页面分离

> 例子
* useTodoList.js

import { ref } from 'vue';
import { message } from 'ant-design-vue';
const useTodoList = function () {

const task = ref("");
const todoList = ref(["吃饭", "睡觉"]);
const addTodoList = () => {
    if (!task.value) {
        message.error('请输入任务名称')
        return;
    }
    if (todoList.value.includes(task.value)) {
        message.error('任务名称重复')
        return;
    }
    todoList.value.push(task.value);
    task.value = "";
}
return {
    task,
    todoList,
    addTodoList
}

}
export default useTodoList

* 使用

<div>
    <label for="task">任务名称:</label>
    <input type="text" v-model="task" id="task" />
    <button class="my-btn" @click="addTodoList()">添加 </button>
    <ul>
        <li v-for="item in todoList">{{ item }}</li>
    </ul>
</div>

***未完待续***