小兔鲜Vue3 项目学习笔记Day01

系列文章目录

小兔鲜Vue3 项目学习笔记 Day01

小兔鲜Vue3 项目学习笔记 Day02

小兔鲜Vue3 项目学习笔记 Day03

小兔鲜Vue3 项目学习笔记 Day04

小兔鲜Vue3 项目学习笔记 Day05

小兔鲜Vue3 项目学习笔记 Day06

前言

vue3现在使用很广泛,必学!

vue3项目,本文主要是学习项目功能实现并进行总结。

配套资料的话去B站下方自己拿。

持续更新…

Day01

1.项目使用相关技术栈

create-vue、Vue3+Setup、VueRouter、VueUse、Pinia

知识点插入:

C端:C是单词Consumer的缩写,即个人用户。C端产品指的是面向个人用户的产品,如微信、淘宝。

B端:B是单词Business的缩写,即企业用户。B端产品指的是面向企业用户的产品,如ERP系统、CRM系统。

2. 项目规模和亮点

项目规模:涉及到9大业务模块,27+业务组件、20+业务接口、10+业务解决方案

项目亮点:长页面吸顶交互实现、图片懒加载指令封装、画板插槽组件等业务通用组件封装、SKU电商组件封装、通用逻辑函数封装、路由缓存问题处理。

3. Vue2和Vue3实现一个小案例

需求描述:点击按钮,数字+1

vue2实现:

//vue2 选项式API
<template>
    <button @click="addCounter">{{ counter }}</button>
</template>

<script>
export default {
    name: 'Counter',
    data() {
        return {
            counter: 0
        }
    },
    methods: {
        addCounter() {
            this.counter++;
        }
    }
}
</script>

vue3实现:

//vue3 组合式API
<template>
    <button @click="addCounter">{{ counter }}</button>
</template>

<script setup>
//有这个setup就可以写组合式api了
import { ref } from 'vue'  //ref 生成响应数据
const counter = ref(0)
const addCounter = () => { counter.value++ }
</script>

vue3的代码量变少了,分散式维护编程了集中式维护了

4. vue3的优势

  • 组合式API,能更好的支持TS,容易维护
  • 速度提高,diff算法重写,模板编译优化,组件初始化更高效了
  • 按需引入,良好的treeShaking,体积更小
  • 数据响应式更优了,proxy

5. create-vue脚手架工具

我们之前在vue2学过使用过vue-cli脚手架

vue3使用的是create-vue,底层是vite,我们使用它来创建项目。

创建环境条件:node.js版本16.0以上

创建vue3应用,安装并执行create-vuenpm init vue@latest

注:目前配置项我们都是否,做项目的时候按需选择。在这里插入图片描述

这样我们就安装成功了,按照它的绿色代码执行(进入文件夹,安装依赖,运行项目),将我们的代码运行起来
在这里插入图片描述
运行起来地址,我们打开:
在这里插入图片描述

如果是这个页面就是成功的,这个页面也是一个很好的学习工具,感兴趣可以点击了解一下:
在这里插入图片描述

6. 熟悉我们的项目目录和文件

package.json:本次项目执行的命令和相关的依赖。
在这里插入图片描述
vite.config.js:项目配置文件,在vue2中是vue.config.js
在这里插入图片描述

业务文件夹src下的main.js入口文件

//入口文件
//vue2中式new Vue()创建一个应用实例对象
//vue3中使用createApp函数创建应用实例
import './assets/main.css'

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

//1.以App为参数,生成一个应用实例对象
//2.挂载到id为app的节点上
createApp(App).mount('#app')

这个节点在哪呢,和vue2一样在index.html

在这里插入图片描述

src中的App.vue

我删了一些代码,结构看起来更清晰

<script setup>
<!--setup:开关,容许在script中书写组合式API-->
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>

<template>
  <header>
    <img  />
    <div class="wrapper"></div>
  </header>
  <main>
    <TheWelcome />
  </main>
</template>

<style scoped>
...
</style>

相比于vue2scripttemplate顺序发生了改变,template里不再要求唯一根元素,省去了一些无用的元素。

7. 组合式API-setup选项

我们先看一些组合式api和选项式api是什么

  • 组合式API是一种基于函数的API,它允许开发者将组件的逻辑代码拆分成多个独立的函数,每个函数负责处理特定的功能或逻辑。

  • 选项式API是基于对象的,我们将一个Vue实例的各个部分拆分成不同的选项,如data、methods、computed、watch等,并在创建Vue实例时将它们作为选项传入。

setup的写法:

<script>
export default {
  setup() {
   ...
  }
}
</script>

这个函数会在生命周期beforeCreate之前执行,验证一下:

<!--App.vue-->
<script>
export default {
  setup() {
    console.log('setup')
  },
  beforeCreate() {
    console.log('beforeCreate')
  }
}
</script>
<template>

</template>
<style></style>

在这里插入图片描述

setup的用法:

  • 在setup定义数据和函数
  • setup函数中 return 数据和函数
  • 在template中使用数据和方法

做个小练习:

<script>
export default {
  setup() {
    //1.声明变量和函数
    const message = '我是一个小蘑菇'
    const logMessage = () => {
      console.log(message)
    }
    return {
      //2.返回变量和函数
      message,
      logMessage
    }
  },
}
</script>
<template>
  //3.使用变量和函数
  {{ message }}
  <button @click="logMessage">点击我</button>
</template>
<style></style>

保存,看效果:

在这里插入图片描述

这里不再推荐使用this,this也获取不到组件实例对象了

这里vue3提供了一个语法糖<script setup>,可以简化代码的写法,将export default、setup()、return都简化掉了

刚才那个栗子使用语法糖的写法:

<script setup>
    const message = '我是一个小蘑菇'
    const logMessage = () => {
      console.log(message)
    }
</script>
<template>
  {{ message }}
  <button @click="logMessage">点击我</button>
</template>

8. 组合式API-reactive和ref函数

作用:生成响应数据

响应式数据:数据变而页面变

reactive:传入对象类型数据,返回一个响应式对象,注意是对象类型

reactive用法:

  • 导入reactive
  • 执行函数(传参,接收)
<script setup>
//引入
import { reactive } from 'vue';
//对象类型变量
const person = {
  name: 'ruru',
  age: 18
}
//使用函数
const res = reactive(person);

</script>
<template>
  <!-- 使用变量 -->
  我是{{ res.name }},我今年{{ res.age }}岁了
  <button @click="res.age++">年龄+1</button>
</template>

在这里插入图片描述

ref:传入简单或对象类型数据,返回一个响应式对象,简单类型和对象类型都支持

ref用法:

  • 导入ref
  • 执行函数(传参,接收)

注意:脚本区域修改ref产生的响应式对象数据(如 a),需要通过value属性。

<script setup>
//引入
import { ref } from 'vue';
//对象类型变量
const name = 'ruru'
const age = 18
const add = () => {
  a.value++
}
//使用函数
const n = ref(name);
const a = ref(age)

</script>
<template>
  <!-- 使用变量 -->
  我是{{ n }},我今年{{ a }}岁了
  <button @click="add">点击a+1</button>
</template>

在这里插入图片描述

ref函数内部实现依赖于reactive,实际工作中推荐使用ref函数,比较灵活

9. 组合式API-computed计算属性

思想和vue2保持一致,只是写法不同

用法:

  • 导入
  • 执行函数,return计算之后的值,变量接收

小栗子实现:[1,2,3,4,5,6,7,8] =》 [3,4,5,6,7,8]

<script setup>
import { computed, ref } from 'vue';
const arr = ref([1, 2, 3, 4, 5, 6, 7, 8]);
const arr2 = computed(() => {
  //ref产生的数据要通过value修改,使用filter函数过滤
  return arr.value.filter(item => item > 2)
})
</script>

<template>
  <div>
    原始响应式数组:{{ arr }}
	<br />
    过滤后的数组:{{ arr2 }}
  </div>
</template>

****

计算属性中不应该有副作用,避免直接修改计算属性的值,计算属性应该是只读

10. 组合式API-watch监听属性

侦听一个或者多个数据的变化,数据变化时执行监听函数

额外参数:immediate(立即执行)、deep(深度监听)

使用:

  • 导入函数
  • 执行watch函数,参数传入响应式数据回调函数

举个 单个数据 的栗子

<script setup>
import { watch, ref } from 'vue';
const count = ref(0)
const setCount = () => {
  count.value++
}
//监听属性watch
watch(count, (newValue, oldValue) => {
    //当count发生改变时打印
  console.log('count改变了',newValue, oldValue)
  
})

</script>

<template>
  <div>
    当前count:{{ count }}
    <button @click="setCount">count+1</button>
  </div>
</template>

在这里插入图片描述

监听 多个数据 的话,只需要改动一下参数,参考代码⬇

多个数据中,只要有一个数据改变了,就会调用回调函数。

watch(
[count,name],
([newCount,newName],[oldCount.oldName]) =>{
	...
	}
)

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

用法示例:

const count = ref(0)
watch(count,()=>{
	console.log('count发生了变化')
},{
immediate : true
})

通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep选项

deep:深层监听

const state = ref({count:0})
watch(count,()=>{
    //count发生变化也不会打印
	console.log('count发生了变化')
})
const state = ref({count:0})
watch(count,()=>{
    //会打印
	console.log('count发生了变化')
},{
    deep:true
})

精确监听

假如目前有两个响应式数据agename,我只想在监听到age数据变化的时候执行回调函数,该怎么办呢?

我们可以这样做:watch参数写两个回调,一个写数据值,另一个写逻辑,大致代码如下:

const person = ref({name:'ruru',age:18})
watch(
	()=>person.age.value,
    ()=> console.log('age改变了')
)

deep有性能损耗,尽量不开启deep,使用 精确监听

11. 组合式API-生命周期函数

vue2和vue3生命周期API(选项式 vs 组合式)

选项式API组合式API
beforeCreate/createdsetupvue3无onCreated钩子,直接写在setup中即可。
beforeMountonBeforeMount挂载之前
mountedonMounted挂载完毕
beforeUpdateonBoforeUpdate更新之前
UpdatedonUpdated更新完毕
beforeUnmountonBeforeUnmount销毁之前
unmountedonUnmounted销毁完毕

基本使用:

  • 导入
  • 执行函数,传入回调

我们以onMounted作栗子来演示生命钩子的使用。

<script setup>
import { onMounted, ref } from 'vue';

onMounted(() => {
  console.log('组件挂载完毕')
})

</script>

<template>
</template>

生命周期函数可执行多次,按顺序执行

一种适用场景是:当你拿到一个写好的项目,不敢改别人的代码怕给整错了,就可以创建一个钩子写入自己的代码,不会影响别人的代码。

<script setup>
import { onMounted, ref } from 'vue';
onMounted(() => {
  console.log('组件挂载完毕1')
})
onMounted(() => {
  console.log('组件挂载完毕3')
})
onMounted(() => {
  console.log('组件挂载完毕2')
})


</script>

<template>
  <div>

  </div>
</template>

在这里插入图片描述

12. 组合式API-父子通信

最常用,最简单

基本思想:

  • 父组件给子组件绑定属性
  • 子组件通过defineProps编译器宏,接收子组件传递过来的数据

现在我们准备了父App.vue和子son.vue组件。

子组件导入父组件之后,不需要注册直接使用,这是setup语法糖的一个效果。

<!-- App.vue -->
<script setup>
import { onMounted, ref } from 'vue';
import Son from '@/components/son.vue'

</script>

<template>
  <div>
    <h2>父组件App</h2>
    <Son />
  </div>
</template>
<!-- son.vue -->
<script setup>

</script>
<template>
    <div class="son">
        <h3>子组件son</h3>
    </div>
</template>

效果:
在这里插入图片描述

现在我们要实现数据的传递:

<!-- App.vue -->
<script setup>
import { onMounted, ref } from 'vue';
import Son from '@/components/son.vue'

</script>

<template>
  <div>
    <h2>父组件App</h2>
    <!-- 1.属性绑定 -->
    <Son message='我是父组件的数据' />
  </div>
</template>
<!-- son.vue -->
<script setup>
const props = defineProps({
    // 2.接收
    message: String
})
</script>
<template>
    <div class="son">
        <h3>子组件son</h3>
        <!-- 3.使用 -->
        {{ message }}
    </div>
</template>

上述代码使用了props变量接收了数据,我们可以props.message获取到数据。

补充:

传入响应式数据,传递多个数据,相关代码演示:

// App.vue 
const count = ref(0)

<Son :count="count" message = '我是父组件的数据' />
//son.vue 
const props = defineProps({
	message:String,
	count:Number
})

13. 组合式API-子传父

基本思想:

  • 父组件给子组件标签通过@绑定事件

  • 子组件通过defineEmits编译器生成emit方法

  • 触发自定义事件,传递参数

通过栗子演示

<!-- App.vue -->
<script setup>
import { onMounted, ref } from 'vue';
import Son from '@/components/son.vue'
const getMessage = (msg) => {
  console.log(msg)
}
</script>

<template>
  <div>
    <h2>父组件App</h2>
    <!--1.绑定事件  -->
    <Son @get-message="getMessage" />
  </div>
</template>
<!-- son.vue -->
<script setup>
//2.生成emit方法
const emit = defineEmits(['get-message'])
//3.触发自定义事件,传参 
const sendMsg = () => emit('get-message', '我是来自子组件的信息,收到请回答!')
</script>
<template>
    <div class="son">
        <h3>子组件son</h3>
        <!-- 4.使用 -->
        <button @click="sendMsg">给父组件发信息</button>
    </div>
</template>

在这里插入图片描述

14.组合式API-模板引用

学过vue2的应该很熟悉

通过ref获取真实的dom对象或者组件示例对象。

获取dom使用方法(组件同理):

  • 调用 ref 函数生成一个 ref 对象
  • 通过 ref 标识绑定 ref 对象到标签
  • 通过value获取标签
<script setup>
import {ref} from 'vue'
//1.调用ref函数
const h1ref = ref(null)

//组件挂载完毕才能获取到dom对象
onMounted(()=>{
    console.log(h1ref.value)  //<h1>我是dom标签h1</h1>
})
</script>
<template>
<!--2.通过ref标识绑定ref对象-->
<h1 ref="h1ref">我是dom标签h1</h1>
</template>

defineExpose:

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

<script setup>
import {ref} from 'vue'
const msg = ref('一段消息')
defineExpose({
    msg
})
</script>

15.组合式API-provide和inject

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

在这里插入图片描述

实现办法:

  • 从vue中引入这俩函数

  • 顶层数据通过provide函数提供数据/响应式数据

provide('key',顶层数据/ref对象)
  • 底层组件通过inject函数获取数据
const message = inject('key')

除了可以传数据,还可以传方法

  • 从vue中引入这俩函数

  • 顶层数据通过provide函数提供函数

const setCount = ()=>{
    count.value++
}
provide('setCount-key',setCount)
  • 底层组件通过inject函数获取函数
const setCount = inject('setCount-key')
  • 就可以在底层组件中使用了
<button @click="setCount">修改顶层组件的count</button>

底层函数想要通知顶层函数组件做修改的话,传递一个方法,底层函数调用方法·

16.综合小案例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

找个文件夹,克隆一下,阅读README.md

安装依赖,启动项目:

npm install
npm run dev

页面就是这样的啦:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们是要实现:渲染、编辑、删除 三个小功能

渲染

思路:声明响应式列表list - 调接口获取数据赋值给list - 绑定给table

const list = ref([])
const getList = async () => {
  const res = await axios.get('/list')
  list.value = res.data  //赋值
}
 <el-table :data=list>
     ...
</el-table>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

删除

思路:获取当前行的id - 调用接口 - 更新数据

<!--利用插槽-->
<template #default="{ row }">
          <el-button type="primary" link>编辑</el-button>
          <el-button type="danger" link @click="delList(row.id)">删除</el-button>
        </template>
const delList = async (id) => {
  await axios.delete(`/del/${id}`)
  getList()
}
编辑

思路:打开弹框 - 回填数据 - 更新数据

点击 编辑 按钮,打开弹框

<el-button type="primary" link @click="onEdit">编辑</el-button>

父组件获取子组件的数据dialogVisible,使用ref获取子组件的示例对象,调用其方法或者属性

const editRef = ref(null)

<Edit ref="editRef" />
const open = ()=>{
	dialogVisible.value = true
}
defineExpose({
    open
})
const onEdit = ()=>{
	editRef.value.open()
}

这时弹框可以正常打开了。

我们在点击按钮调用onEdit,将数据给Edit组件传下去

<el-button type="primary" link @click="onEdit(row)">编辑</el-button>
const onEdit = (row)=>{
	editRef.value.open(row)
}
const open = (row)=>{
	dialogVisible.value = true
	console.log(row)
}

这样子组件就拿到了数据

准备一个Edit组件的响应式数据form,用来绑定数据

这个属性名称要参考下面的姓名和籍贯书写

const form = ref({
	name:'',
	place:''
})
const open = (row)=>{
	dialogVisible.value = true
	//console.log(row)
    form.value.name = row.name
   	form.value.place = row.place
}

双向绑定:

 <el-form label-width="50px">
      <el-form-item label="姓名">
        <el-input placeholder="请输入姓名" v-model="form.name"/>
      </el-form-item>
      <el-form-item label="籍贯">
        <el-input placeholder="请输入籍贯" v-model="form.place"/>
      </el-form-item>
    </el-form>

更新数据,先调接口,关闭弹窗,通知父组件更新列表

<el-button type="primary" @click="onUpdate">确认</el-button>
import axios from 'axios';
const onUpdate = async ()=>{
	await axios.patch(`/edit/${id}`, {
  	name: form.value.name, 
  	place: form.value.place,
 })
}

要传入id,我们补一个id 上面的 ${id} => ${form.value.id},姓名和籍贯都改改,async,await也写上。

const form = ref({
	name:'',
	place:'',
	id:''
})
const open = (row)=>{
	dialogVisible.value = true
	//console.log(row)
    form.value.name = row.name
   	form.value.place = row.place
   	form.value.id = row.id
}

关闭弹窗

dialogVisible.value = false

通知父组件做列表更新,是子传父,我们使用自定义事件。

<!--绑定事件,将 获取列表数据 的函数传过去-->
<Edit ref="editRef" @on-update="getList"/>
const emit = defineEmits(['on-update'])

emit('on-update')

这样就完成了

项目相关全部代码:

<!-- App.vue -->
<script setup>
import Edit from './components/Edit.vue'
import { onMounted, ref } from 'vue';
import axios from 'axios'

// TODO: 列表渲染
const list = ref([])
const getList = async () => {
  const res = await axios.get('/list')
  list.value = res.data  //赋值
}

onMounted(() => getList())
// TODO: 删除功能
const delList = async (id) => {
  await axios.delete(`/del/${id}`)
  getList()
}

// TODO: 编辑功能
//思路:打开弹框 - 回填数据 - 更新数据
const editRef = ref(null)

const onEdit = (row) => {
  editRef.value.open(row)
}
</script>

<template>
  <div class="app">
    <el-table :data=list>
      <el-table-column label="ID" prop="id"></el-table-column>
      <el-table-column label="姓名" prop="name" width="150"></el-table-column>
      <el-table-column label="籍贯" prop="place"></el-table-column>
      <el-table-column label="操作" width="150">
        <template #default="{ row }">
          <el-button type="primary" link @click="onEdit(row)">编辑</el-button>
          <el-button type="danger" link @click="delList(row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
  <Edit ref="editRef" @on-update="getList" />
</template>

<style scoped>
.app {
  width: 980px;
  margin: 100px auto 0;
}
</style>

<!-- Edit.vue -->
<script setup>
// TODO: 编辑
import { ref } from 'vue'
import axios from 'axios';
// 弹框开关
const dialogVisible = ref(false)
const emit = defineEmits(['on-update'])
const form = ref({
  name: '',
  place: '',
  id: ''
})
const onUpdate = async () => {
  await axios.patch(`/edit/${form.value.id}`, {
    name: form.value.name,
    place: form.value.place,
  })
  dialogVisible.value = false  //关闭弹窗
  emit('on-update')
}
const open = (row) => {
  dialogVisible.value = true
  form.value.name = row.name
  form.value.place = row.place
  form.value.id = row.id
}
defineExpose({
  open
})
</script>

<template>
  <el-dialog v-model="dialogVisible" title="编辑" width="400px">
    <el-form label-width="50px">
      <el-form-item label="姓名">
        <el-input placeholder="请输入姓名" v-model="form.name" />
      </el-form-item>
      <el-form-item label="籍贯">
        <el-input placeholder="请输入籍贯" v-model="form.place" />
      </el-form-item>
    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="onUpdate">确认</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<style scoped>
.el-input {
  width: 290px;
}
</style>

Day01小结

第一天,讲的是vue3的一些基础知识,做了一个小练习
love and peace
在这里插入图片描述

  • 33
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
小兔项目是一个使用Vue3.0开发的项目。它采用了vuex来管理购物车和用户数据,使得数据的管理更加方便。项目中会使用到vue3.0的新特性,因此会体验到更加嫩、酸爽的开发过程。然而,由于目前大多数第三方UI组件库不支持vue3.0,所以在项目中会大量自己封装和布局组件,并预制一些基本样式。在解决问题时,小兔项目主要使用ref函数和组合式API - computed,这样可以更加灵活地处理项目中的数据。关键文件包括vite.config.js(项目的配置文件,基于vite的配置)、package.json(项目包文件,核心依赖项变成了Vue3.x和vite)、main.js(入口文件,使用createApp函数创建应用实例)和app.vue(根组件,使用SFC单文件组件,包含script、template和style)。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [小兔Vue3.0前端电商项目实战](https://blog.csdn.net/qzc2017/article/details/120634549)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [小兔项目----vue3入门](https://blog.csdn.net/qq_63358859/article/details/130704712)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值