【coderwhy前端笔记 - 阶段六 VUE 】(整理版)(更新中2023.7.16)

coderwhy前端系统课 - 阶段六VUE (整理版)(更新中2023.7.16)

1. 前言

本文以coderwhy前端系统课 - 阶段六VUE为主。

一刷版本的笔记有些乱,目前还在二刷整理,同时参考了一部分其他的资料,并加上个人使用总结

建议使用资源绑定里的html文件进行阅读

资源说明:

  1. 资源内有自定义的样式更便于阅读,这里的样式不做额外编写
  2. 资源内点击侧边栏开关时文章阅读位置会偏移,点击目录定位即可
  3. 资源内的图片无法像csdn的可以点击放大,但能看清的
  4. 该文章有些定位点链接,资源内可点击,文章这点了没效果,不做额外修改
  5. 该文章出现一些看不懂的符号拼接啥的,略过
    (一些自定义语法,太多了,这回应该删完了,后面更新估计懒得删)

附上一张效果图:
在这里插入图片描述

2. vue2 vs vue3

放在开头为了方便对比,后面的内容以vue3为主

2.1. vue文件结构

vue2

<template><div><h1>{{ title }}</h1><button @click="increment">{{ count }}</button></div>
</template><script>
export default {data() {return {title: 'Hello, Vue2!',count: 0}},methods: {increment() {this.count++}}
}
</script>

vue3

<template><div><h1>{{ title }}</h1><button @click="increment">{{ count }}</button></div>
</template><script>
import { ref } from 'vue'export default {data() {return {// 也可以在这定义data数据}},setup() {const title = 'Hello, Vue3!'const count = ref(0)function increment() {count.value++}return {title,count,increment}}
}
</script>

2.2. 路由

– 来自chatgpt,两者都使用Vue Router来实现路由功能

vue2

  1. 引入Vue Router插件及其依赖
import Vue from 'vue'
import Router from 'vue-router'Vue.use(Router)
  1. 配置路由映射关系
const router = new Router({routes: [{path: '/',name: 'home',component: Home},{path: '/about',name: 'about',component: About}]
})

其中,path表示URL路径,name为路由名称,component指定该路由对应的组件。

  1. 将路由挂载到Vue实例上
new Vue({router,render: h => h(App),
}).$mount('#app')

在 Vue3 中,render 函数的写法有所不同,使用了新的 createApp API。

vue3

提供了一个基于函数式API的新特性:createRouter()

  1. 引入Vue Router插件及其依赖
import { createRouter, createWebHistory } from 'vue-router'
  1. 创建Router实例并配置路由映射关系
const routes = [{path: '/',name: 'home',component: Home},{path: '/about',name: 'about',component: About}
]const router = createRouter({history: createWebHistory(),routes
})

与Vue2不同的是,在Vue3中,需要将history传递给 createWebHistory 来指定路由模式。

routes 数组中的每个对象包含pathnamecomponent属性。

  1. 将路由挂载到Vue实例上
createApp(App).use(router).mount('#app')

这里使用createApp()方法来创建Vue实例,并通过use()方法将router实例添加到Vue实例中。

备注

vue2 中的 render: h => h(App)

20230503101058

2.3. JS函数

vue2

3. 基础语法

3.1. 插值语法 {{}}

html

<!-- 1.基本使用 -->
<h2>{{ message }}</h2>
<h2>当前计数: {{ counter }} </h2><!-- 2.表达式 -->
<h2>计数双倍: {{ counter * 2 }}</h2>
<h2>展示的信息: {{ info.split(" ") }}</h2><!-- 3.三元运算符 -->
<h2>{{ age >= 18? "成年人": "未成年人" }}</h2><!-- 4.调用methods中函数 -->
<h2>{{ formatDate(time) }}</h2>

javascript

 //datacounter: 100,info: "my name is why",age: 22,time: 123//methodformatDate: function(date) {return "2022-10-10-" + date}

3.2. 内置指令

  • v-for(item in list) 遍历,内容:{{item}}
  • v-text 文本 ,等于直接用 {{}}
  • v-html  HTML插入
  • v-pre  无需编译,如{{m}},显示在浏览器还是 {{m}}
  • v-once  只传入一次
  • v-cloak 遮罩斗篷,先隐藏没有传入值的{{m}},有传入值后显示,css加上 [v-cloak]{display:none}

(1) v-memo 某值改变才更新

某值改变时才更新内容,可查看《vue3.2新增指令v-memo的使用 - 南风晚来晚相识》

html

<div id="app"><div v-memo="[name]"><h2>姓名: {{ name }}</h2><h2>年龄: {{ age }}</h2><h2>身高: {{ height }}</h2></div><button @click="updateInfo">改变信息</button>
</div>

javascript

data: function() {return {name: "why",age: 18,height: 1.88}
},methods: {updateInfo: function() {this.name = "kobe"this.age = 20}
}

(2) v-bind 动态绑定 :

原语句语法糖
v-bind:src="":src
v-bind:href="":href
v-bind:class="":class

绑定方法参考

html

<!-- 1.绑定img的src属性 -->
<img v-bind:src="showImgUrl" alt="">
<!-- 语法糖: v-bind -> : -->
<img :src="showImgUrl" alt=""><!-- 2.绑定a的href属性 -->
<a :href="href">百度一下</a>

javascript

data: function() {return {imgUrl1: "http://p1.music.126.net/agGc1qkogHtJQzjjyS-kAA==/109951167643767467.jpg",imgUrl2: "http://p1.music.126.net/_Q2zGH5wNR9xmY1aY7VmUw==/109951167643791745.jpg",showImgUrl: "http://p1.music.126.net/_Q2zGH5wNR9xmY1aY7VmUw==/109951167643791745.jpg",href: "http://www.baidu.com"}
},methods: {switchImage: function() {//  图片地址 =  现在的图片地址 ===  是图片1吗 ?     是:展示图片2  不是:显示图片1this.showImgUrl = this.showImgUrl === this.imgUrl1 ? this.imgUrl2: this.imgUrl1}
}
❥ 绑定class

前提:

 // css.active {color: red;}//dataisActive: false,// methodsbtnClick: function() {this.isActive = !this.isActive},
  1. 三元:
  <!--		=isActive吗?   	是:添加active样式   否:啥也不加			--><button :class="isActive ? 'active': ''" @click="btnClick">我是按钮</button>
  1. 单值 ⭐
<!--	不会影响原有class	 active样式加不加呢?    =false 不加    =true 加		-->
<button class="haha" :class="{ active: isActive }" @click="btnClick">我是按钮</button>
  1. 对象语法的多个键值对
<!--								直接添加 why		-->
<button :class="{ active: isActive, why: true, kobe: false }" @click="btnClick">我是按钮</button>
  1. 把 :class 的内容抽取出去
<!--							抽取到方法里			-->
<button class="abc cba" :class="getDynamicClasses()" @click="btnClick">我是按钮</button>
 //methodgetDynamicClasses: function() {return { active: this.isActive, why: true, kobe: false }}
  1. 数组
    <!-- 3.动态class可以写数组语法(了解) --><h2 :class="['abc', 'cba']">Hello Array</h2><h2 :class="['abc', className]">Hello Array</h2><h2 :class="['abc', className, isActive? 'active': '']">Hello Array</h2><h2 :class="['abc', className, { active: isActive }]">Hello Array</h2>
❥ 绑定style
  1. 绑定属性为对象,分隔符为,
<!--  普通的html写法 -->
<h2 style="color: red; font-size: 30px;">哈哈哈哈</h2><!--  对象类型  -->
<!--			调用data的fontColor		调用data的fontSize与px拼接	88px要连在一起,所以加''	-->
<h2 v-bind:style="{ color: fontColor, fontSize: fontSize + 'px', height: '88px' }">哈哈哈哈</h2>
//data
fontColor: "blue",
  1. 绑定属性为数组 (很少用)
<h2 :style="objStyle">呵呵呵呵</h2>
<h2 :style="[objStyle, { backgroundColor: 'purple' }]">嘿嘿嘿嘿</h2>
//data
objStyle: {fontSize: '50px',color: "green"
}
❥ 绑定属性名
<h2 :[name]="'aaaa'">Hello World</h2>
//data
name: "class"
❥ 绑定对象
<h2 v-bind="infos">Hello Bind</h2>
// data
infos: { name: "why", age: 18, height: 1.88, address: "广州市" },

(3) v-on 事件绑定(事件监听) @

原语句语法糖
v-on:click=" "@click=" "

绑定方法参考

<!-- 1.基本的写法 -->
<div class="box" v-on:click="divClick"></div><!-- 2.语法糖写法(重点掌握) -->
<div class="box" @click="divClick"></div><!-- 4.绑定其他方法(掌握) -->
<div class="box" @mousemove="divMousemove"></div><!-- 5.元素绑定多个事件(掌握) -->
<div class="box" @click="divClick" @mousemove="divMousemove"></div>
methods: {divClick() {console.log("divClick")},divMousemove() {console.log("divMousemove")}
}
❥ 参数传递
<!-- 1.默认传递event对象 -->
<button @click="btn1Click">按钮1</button><!-- 2.只有自己的参数 -->
<button @click="btn2Click('pyy', age)">按钮2</button><!-- 3.自己的参数和event对象在模板中想要明确的获取event对象: $event -->
<button @click="btn3Click('pyy', age, $event)">按钮3</button>
//data
age: 18
//方法 
methods: {// 1.默认参数: event对象// 总结: 如果在绑定事件的时候, 没有传递任何的参数//       那么event对象会被默认传递进来btn1Click(event) {console.log("btn1Click:", event)},// 2.明确参数:btn2Click(name, age) {console.log("btn2Click:", name, age) // pyy 18},// 3.明确参数+event对象btn3Click(name, age, event) {console.log("btn3Click:", name, age, event)}
}
❥ 添加修饰符
<button @click.stop="btnClick">按钮</button>

使用过

回车自动触发:@keyup.enter.native
阻止默认事件:@submit.native.prevent

解决问题:
20230509005949

其他修饰符

  • .stop - 调用 event.stopPropagation(),这是阻止事件的冒泡方法,不让事件向document上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开,解释来源
  • .prevent - 调用event.preventDefault(),这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;
  • .capture - 添加事件侦听器时使用 capture 模式,事件冒泡
  • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调
  • .{keyAlias} - 仅当事件是从特定键触发时才触发回调
  • .once - 只触发一次回调
  • .left - 只当点击鼠标左键时触发
  • .right - 只当点击鼠标右键时触发
  • .middle - 只当点击鼠标中键时触发passive -{ passive: true}模式添加侦听器

(4) v-if 是否(条件渲染)

v-if="条件"     条件成立,执行该段
v-else       条件不成立,执行该段

❥ 数组
 <ul v-if="names.length > 0"><li v-for="item in names">{{item}}</li></ul><h2 v-else>当前names没有数据, 请求获取数据后展示</h2>

v-for(元素 in 列表)  遍历

 //datanames:[]  // 无数据names:[ab, ad, ae] // 有数据
❥ 对象
<!-- v-if="条件" 	 	无值 > false     有值 > true		 -->
<div class="info" v-if="Object.keys(info).length">  <h2>个人信息</h2><ul><li>姓名: {{info.name}}</li><li>年龄: {{info.age}}</li></ul>
</div><!-- v-else -->
<div v-else><h2>没有输入个人信息</h2><p>请输入个人信息后, 再进行展示~</p>
</div>
info: {name:"aa", age:11}
❥ if else if
  <div id="app"><h1 v-if="score > 90">优秀</h1><h2 v-else-if="score > 80">良好</h2><h3 v-else-if="score >= 60">及格</h3><h4 v-else>不及格</h4></div>

(5) v-show 显示/隐藏 (条件渲染)

<div><button @click="toggle">切换</button>
</div><div v-show="isShowCode"><img src="https://game.gtimg.cn/images/yxzj/web201706/images/comm/floatwindow/wzry_qrcode.jpg" alt="">
</div>
// data
isShowCode: true
//method
toggle() {this.isShowCode = !this.isShowCode
}

注意:
v-if=true/false也可以控制元素的隐藏显示,区别在于:

  • v-show 不支持 template (见template详解)
  • v-show 不可以和 v-else 一起使用
  • v-show 不管是 true 还是 false ,内容的 dom 都是存在的,只是通过 css 的 display 属性来切换
  • v-if=flase 时,对应的内容不会渲染在 dom 中(是不存在的!
  • 频繁的切换隐藏显示用v-show

(6) v-for 遍历 (列表渲染)

v-for="(item,index) in 数组"

也支持 v-for="(item,index) of 数组",但平时一般直接用in

《v-for 循环中 in 与 of 区别,以及 ES5 for in 与 ES6 for of 区别 - 雁 南飞》:

20230520135035

❥ 数组和对象

/–遍历对象 v-for="(value, key, index) in info"

/–遍历字符串 v-for="item in message"

/–遍历数字 v-for="item in 100"

写在 li 标签,会产生多个 li,默认 item 为 value 值

html

<!--  遍历 value  -->
<li v-for="movie in movies">{{ movie }}</li><!-- 遍历 value, key, index -->
<li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}
</li><!-- 有索引   0, 1, 2, 3...     -->
<li v-for="(movie, index) in movies">{{index + 1}} - {{ movie }}
</li><!-- 3.遍历数组复杂数据 -->
<h2>商品列表</h2>
<div class="item" v-for="item in products"><h3 class="title">商品: {{item.name}}</h3><span>价格: {{item.price}}</span><p>秒杀: {{item.desc}}</p>
</div>
</div>

javascript

 // data// 1.moviesmovies: ["星际穿越", "少年派", "大话西游", "哆啦A梦"],// 2.数组: 存放的是对象products: [{ id: 110, name: "Macbook", price: 9.9, desc: "9.9秒杀, 快来抢购!" },{ id: 111, name: "iPhone", price: 8.8, desc: "9.9秒杀, 快来抢购!" },{ id: 112, name: "小米电脑", price: 9.9, desc: "9.9秒杀, 快来抢购!" },]
❥ 数组更新检测
 changeArray() {// 1.直接将数组修改为一个新的数组// this.names = ["why", "kobe"]// 2.通过一些数组的方法, 修改数组中的元素this.names.push("why")    // 加this.names.pop()          // 删除后面一个this.names.splice(2, 1, "why")this.names.sort()this.names.reverse()// 3.不修改原数组的方法是不能侦听(watch)// 因为this.names.map() 会产生一个新数组,而不是修改原数组,所以需要把值存入一个变量const newNames = this.names.map(item => item + "why")  //每个value后面拼接why    this.names = newNames}

⭐ splice!可以添加、删除、替换

names.splice(3,1,"pyy","pyyyy") // 在位置3 删除1个 添加pyy和pyyyy
❥ key的作用

官方解释key::
在这里插入图片描述

有key和没有key时执行的方法不一样
20230516144301

没key时,也会执行diff算法,对比遇到不一样的内容时,后面的全部替换:

有key时,也使用diff算法,但会尽量的复用原有节点:
20230516144534

<!-- key要求是唯一: id -->
<li v-for="item in letters" :key="item">{{item}}</li>

总结
20230520135653

key的面试问答

(7) template (列表渲染)

当div没有意义,又使用了v-xxx,那就把 div 换成 template
可以放id <template id=" ">

在这里插入图片描述

以下来自解释来自于《vue v-if与v-show的区别,template的使用 - 键盘上的那抹灰》

  • 当两个以上的div被同一元素控制时,用 template 替换该元素
  • 控制台template改良版将不会出现一个新的div,也不会出现template,减少空间
  • template是没有实际东西的dom,所以v-show与template联合使用将失效

3.3. v-model

原理和使用方法

原理:
在这里插入图片描述

写法:

✗ 手动绑定,先显示message值在input里,input输入其他值时,message同步改变

<input type="text" :value="message" @input="inputChange">
<h2>{{message}}</h2>
 //datamessage: "Hello Model",// methodsinputChange(event) {this.message = event.target.value},

⭐ v-model,不用添加方法

<input type="text" v-model="message">
<h2>{{message}}</h2>

案例:登录

    <label for="account">账号:<input id="account" type="text" v-model="account"></label><label for="password">密码:<input id="password" type="password" v-model="password"></label><button @click="loginClick">登录</button>
// data 
account: "",
password: ""
// methods
loginClick() {    // 获取值,发送出去const account = this.accountconst password = this.password// url发送网络请求console.log(account, password)
}

(1) 绑定到 textarea

在这里插入图片描述

(2) 绑定到 checkbox

在这里插入图片描述

lable 里的 for 为 html 内的知识点,点击文字也可以关联到 input

(3) 绑定到 redio

在这里插入图片描述

(4) 绑定到 select

在这里插入图片描述

(5) 值绑定

选项也来自服务器时。
请添加图片描述

(6) 修饰符

v-model-lazy 会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发

v-model-number 转为数字类型

v-model-trim 动过滤用户输入的空白字符

使用多个

v-model.lazy.trim="" 可以组合使用

4. Options API(vue2)

Options API 包含

 export default {data() { return { }  },  // 数据   props: [ ]               // 接收父组件传递过来的属性methods:{ },            // 方法watch: { },              // 监听(数据变化)computed: { },           // 复杂数据处理minxins: { },            // 混入(合并),vue2用的多components: { },         // 局部组件created() { },           // 监听(创建后)
+// lifecycle hooks 生命周期钩子函数,如// created、mounted、updated、destroyed 等// 以下不确定 provide: [ ]             // 提供数据给injectinject: [ ]              // 使用props这些数据}

4.1. 复杂数据处理 ⭐computed

(1) 方案对比

  1. 插值语法 
    <!-- 插值语法表达式直接进行拼接 --><!-- 1.拼接名字 --><h2>{{ firstName + " " + lastName }}</h2><!-- 2.显示分数等级 --><h2>{{ score >= 60 ? '及格': '不及格' }}</h2><!-- 3.反转单词显示文本 --><h2>{{ message.split(" ").reverse().join(" ") }}</h2>

/–split 将字符串转化为数组
/–reverse 反转
/–join 用xx拼接

  1. method 方法 

函数调用

    <!-- 1.拼接名字 方便多次调用--><h2>{{ getFullname() }}</h2><h2>{{ getFullname() }}</h2><h2>{{ getFullname() }}</h2><!-- 2.显示分数等级 --><h2>{{ getScoreLevel() }}</h2><!-- 3.反转单词显示文本 --><h2>{{ reverseMessage() }}</h2>
 // methodsgetFullname() {return this.firstName + " " + this.lastName},getScoreLevel() {return this.score >= 60 ? "及格": "不及格"},reverseMessage() {return this.message.split(" ").reverse().join(" ")}

弊端:所有的data使用过程都变成了方法的调用

  1. computed 计算属性 ⭐

官方:任何包含响应式数据的复杂逻辑,都应该使用计算属性(案例里都算相应式数据的复杂逻辑)

数据更新时会自动处理

computed 位置

 const app = Vue.createApp({data() { return { }  },methods:{ },computed: { }}).mount("#app")

处理案例:

    <!-- 1.拼接名字 --><h2>{{ fullname }}</h2><h2>{{ fullname }}</h2><h2>{{ fullname }}</h2><!-- 2.显示分数等级 --><h2>{{ scoreLevel }}</h2><!-- 3.反转单词显示文本 --><h2>{{ reverseMessage }}</h2>
 // 1.创建appconst app = Vue.createApp({// data: option apidata() {return {// 1.姓名firstName: "kobe",lastName: "bryant",// 2.分数: 及格/不及格score: 80,// 3.一串文本: 对文本中的单词进行反转显示message: "my name is why"}},computed: {// 1.计算属性默认对应的是一个函数fullname() {return this.firstName + " " + this.lastName},scoreLevel() {return this.score >= 60 ? "及格": "不及格"},reverseMessage() {return this.message.split(" ").reverse().join(" ")}}})// 2.挂载appapp.mount("#app")

(2) method 与 computed 区别

  • 表现形式:computed 稍微简洁一些
    在这里插入图片描述

  • computed 有缓存
    在这里插入图片描述
    解释:第一次变化时,数据未发生变化,computed方法仅调用了一次,缓存下来直接用,而method方法重复调用(执行)了三次

(3) 计算属性的 set get

20230517145402

4.2. 监听器 watch

位置

 const app = Vue.createApp({data() { return { }  },methods:{ },computed: { },watch:{ }}).mount("#app")

(1) 监听新旧值

dataName(newValue, oldValue) {}

 const app = Vue.createApp({// data: option apidata() {return {message: "Hello Vue",info: { name: "why", age: 18 }}},methods: {changeMessage() {this.message = "你好啊, 李银河!"this.info = { name: "kobe" }}},watch: {// 1.默认有两个参数: newValue/oldValuemessage(newValue, oldValue) {console.log("message数据发生了变化:", newValue, oldValue)},info(newValue, oldValue) {// 2.如果是对象类型, 那么拿到的是代理对象console.log("info数据发生了变化:", newValue, oldValue) // 两个(proxy)对象 // newValue proxy对象 {name:"kobe"}// oldValue proxy对象  { name: "why", age: 18 }console.log(newValue.name, oldValue.name)    // 两个值// 3.获取原生对象  (不想获取proxy对象,想要获取的原生对象方法)console.log(...newValue)              // 原生的方法console.log(Vue.toRaw(newValue))      //vue专门提供的方法,{name:kobe}}}}).mount("#app")

如果原来是对象类,那么监听时获取到的也是proxy对象

对象类型

proxy对象 👉 Vue.toRaw(newValue)

(2) 深度监听

methods方法修改内容时,虽然内容会有变化,但是默认的watch不会进行深度监听!,所以在watch里默认没有监听到

修改info.name,监听变化

在这里插入图片描述

  • handler(){}相当于info(newValue,oldValue){}的语法糖
  • deep: true 启动深度监听
  • 但!点击改变 info.name:kobe 后改深度对象返回的newValue,oldValue是相同的
    20230520015729
    因为改变的不是info对象是info里的属性, (所以也没能监听到啊,deep:true到底有什么用)
  • immediate,第一次渲染时执行一次
  • 这里的 info.name function 是vue2的知识点,能监听到name的变化

(3) 声明周期的监听

监听message

 const app = Vue.createApp({// data: option apidata() {return {message: "Hello Vue"}},methods: {changeMessage() {this.message = "你好啊, 李银河!"}},// 生命周期回调函数: 当前的组件被创建时自动执行// 一般在该函数中, 会进行网络请求created() {// ajax/fetch/axiosconsole.log("created")this.$watch("message", (newValue, oldValue) => {console.log("message数据变化:", newValue, oldValue)}, { deep: true })}})

4.3. Options API的弊端

vue2编写组件的方式是Options API

  • Options API一大特点是在对应的属性中编写对应的功能模块
  • data定义数据methods定义方法computed定义计算属性watch监听属性变化,也包括生命周期钩子

但是这种代码有很大的弊端

  • 当实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中
  • 当组件变得更大、更复杂时,逻辑关注点的列表就会增长,那么同一个功能的逻辑就会被拆分的很分散
  • 对于一开始没有编写这些组件的人(包括阅读组件的其他人)来说,这个组件的代码是难以阅读和理解

20230705070008

当组件非常大时,这种碎片化的代码使用、理解和维护这个复杂的组件变得异常困难,并且隐藏了潜在的逻辑问题。并且处理单个逻辑关注点时,需要不断地跳转到相应的代码中

5. Composition API (vue3)

5.1. 含义和区别

Composition API是什么,跟options API有什么区别 —— chatgpt

Composition API是Vue 3中提供的一种新的API风格,用于组织和重用组件逻辑。与Options API不同,Composition API将相关的代码组合在一起,以便更好地组织和重用代码。它基于函数而不是对象的形式,使得代码更容易拆分成可组合的逻辑块。此外,使用Composition API可以更好地封装和隐藏信息,并在组件之间共享逻辑,从而提高代码的可维护性和可重用性。总之,Composition API是Vue 3中一个非常实用且令人期待的改进,可以帮助我们更轻松地构建复杂的Web应用程序。

5.2. 包含内容

Composition API包含什么 —— chatgpt

  1. reactive:用于将一个普通对象转换为响应式对象,可以监听该对象属性的变化。

  2. ref:用于将基本类型数据转换为响应式对象,可以监听该值的变化。

  3. computed:用于创建计算属性,依赖于其他响应式对象和计算属性。

  4. watchEffect:用于监听响应式对象的变化,并在变化时执行回调函数。

  5. 生命周期钩子:包括onMounted(挂载到DOM后)、onUpdated(更新后)、onUnmounted(卸载后)等,用于在组件生命周期的不同阶段执行相应的操作。

  6. provideinject:用于跨层级传递数据。

  7. 封装复用逻辑:通过使用函数封装可复用的逻辑,实现组件之间逻辑共享。

5.3. setup函数

(1) 参数

props 和 context

props 从父组件传递过来的属性会被放在props对象中,setup中需要使用时直接通过props参数获取:

  • 定义props的类型和之前的规则是一样的,再props选项中定义
  • 再template中依然是可以正常使用props中的属性
  • 再setup函数中想要使用props,不可以通过this去获取
  • 因为props有直接作为参数传递到setup函数中,可以直接通过参数来使用即可

context 也称之为SetupContext,包含三个属性:

  • attrs:所有的非props的attribute
  • slots:父组件传递过来的插槽
  • emit:组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过this.$emit发出事件)

案例

计数器
20230707054946

设:计数器代码需要复用,要将相关代码提取出来
20230707055728

总结:使用setup方法,函数的复用性、简洁性更强

(2) 返回值 return

作用

  • 可以在模板template中被使用
  • 可以通过setup的返回值来替代data选项
  • 可以返回一个执行函数来替代在methods中定义的方法👇
    20230712063848

注意: 此时counter不是响应式数据,因为对于一个定义的变量来说,默认情况下,Vue并不会跟踪它的变化来引起界面的响应式操作

(3) 数据响应式

❥ Reactive API
  • 对传入类型有限制,必须是一个对象或者数组

  • 一般用在复杂类型的数据,如账号密码

  • 传入基本数据类型(String、Number、Boolean)时会有警告

<template>{{state.name}}{{state.counter}}
</template>
import {reactive} from 'vue'
export default {setup() {const state = reactive({name:'pyy',counter: 100})return {name,counter}}
}

原理:

  • 这是因为当我们使用reactive函数处理我们的数据之数据再次被使用时就会进行依赖收集
  • 数据发生改变时,所有收集到的依赖都是进行对应的响应式操作(比如更新界面);
  • 事实上,我们编写的data选项,也是在内部交给了reactive函数将其编程响应式对象的;
❥ Ref API
  • ref会返回一个可变的响应式对象

  • 该对象作为一个响应式的引用,维护着它内部的值,这就是ref名称的来源

  • 它内部的值是在ref的value属性中被维护着

  • 在模板(template)引入ref时,vue会自动解包(浅层解包),所以在模板中不需要写ref.value

<template><div class='app'>{{counter}}  <!-- 会自动解包 --></div>
</template><script>
import { ref } from 'vue'export default {setup() {let counter = ref(100)const increment = () => {counter.value++   // 表示ref.value}const decrement = () => {counter.value--}return { counter, increment, decrement }}
}</script>

一点小瑕疵

“ref是浅层解包”说法里有点小瑕疵,估计是

20230712110724

相关js👇

20230712111444

const info = {counter // 语法糖写法,全称为 counter: counter,表示counter的值为上方的counter = ref(0)
}
❥ 用哪个

/–ref 可以定义简单的数据,也可以定义复杂的数据

方法定义的数据应用场景
ref简单与复杂数据都可以1、其他的场景基本都用ref
2、定义从网络中获取的数据(案例👇)
reactive复杂数据1、本地(生成的)数据,如:账号密码
2、多个数据之间是有关系的,组合在一起有特定联系(表单)

案例

// 2.定义从网络中获取的数据也是使用ref
// const musics = reactive([])  但是一般用ref
const musics = ref([])
onMounted(() => {const serverMusics = ["晴天", "屋顶", "听妈妈的话"]musics.value = serverMusics
})

(4) reactive 知识补充 —— readonly

readonly:一般我们通过reactiveref可以获取到一个响应式的对象。但某些情况下,我们传给其他组件的这个响应式对象希望另外一个组件被使用,但不能被修改。(了解即可)

(👆总结:响应式对象在其他组件可以被使用,但不能修改

readonly会返回原始对象的只读代理(也就是它依然是一个Proxy)

单项数据流:组件数据传到另一项组件时为只读状态

react:react的使用是非常灵活的,但是它有一个重要的原则 —— 任何一个组件都应该像函数一样,不能修改传入的props

2023.7.16 更新到这

6. 组件(一)

概念及思想

2023052204403620230522043929

6.1. 注册 调用

全局组件
在任何其他的组件中都可以使用的组件,app.component( 组件名称, 对象 ) 方法

局部组件
只有在注册的组件中才能使用的组件,components( 组件名称, 对象 ) 属性

在这里插入图片描述

(1) 全局组件

app.component( 组件名称, 对象 ) 方法,注册全局组件,html 里直接用组件名<组件名称></组件名称>

⭐ vue全局注册案例 —— chatgpt

1.假设你有一个名为"my-component"的组件

<template><div>{{ message }}</div>
</template><script>
export default {data() {return {message: 'Hello World!'}}
}
</script>

2.要在整个应用程序中使用该组件,你需要先调用Vue全局方法"Vue.component()"进行注册,如下所示:

// main.js
import Vue from 'vue'
import MyComponent from './MyComponent.vue'Vue.component('my-component', MyComponent)new Vue({el: '#app',// ...
})

现在,可以在整个应用程序中使用"my-component"组件,如下所示:

// App.vue
<template><div id="app"><my-component></my-component></div>
</template><script>
import MyComponent from './MyComponent.vue'export default {components: {MyComponent}
}
</script>

在这个例子中,我们已经在"main.js"文件中全局注册了"my-component"组件,因此我们不再需要在"App.vue"文件中再次导入和注册该组件。相反,我们可以直接在模板中使用该组件。

总结一下,Vue的全局组件注册使得我们可以在整个应用程序中使用该组件,而不需要重复导入或注册该组件。

课程内容

框架
在这里插入图片描述

案例
在这里插入图片描述

案例
在这里插入图片描述

全局组件的特点: 一旦注册成功后, 可以在任意其他组件的template中使用
在这里插入图片描述

以前写法对比图:
在这里插入图片描述

案例:

在这里插入图片描述

(2) 局部组件

components( 组件名称, 对象 ) 属性

 const app = Vue.createApp({components: { },    // 局部组件data() { return { }  },  // 数据methods:{ },     // 方法computed: { },    // 复杂数据处理watch:{ }      // 监听}).mount("#app")

⭐ vue局部注册案例 —— chatgpt

以下是一个简单的 Vue 局部组件注册案例:

<template><div><!-- 使用局部组件 --><my-component></my-component></div>
</template><script>
import MyComponent from './MyComponent.vue'export default {components: {// 注册局部组件'my-component': MyComponent}
}
</script>

在上面的代码中,我们首先引入了 MyComponent 组件,然后在 Vue 实例的 components 选项中注册了该组件。这个组件现在就可以在当前 Vue 组件中使用,并且可以通过 标签来调用。

需要注意的是,在进行局部组件注册时,我们需要指定组件的名称,这个名称将作为组件的标签名在模板中使用。在上面的例子中,我们注册了名为 my-component 的组件,并在模板中使用了它。

值得一提的是,Vue 还支持使用字符串数组来一次性注册多个局部组件,例如:

<script>
import MyComponent1 from './MyComponent1.vue'
import MyComponent2 from './MyComponent2.vue'
import MyComponent3 from './MyComponent3.vue'export default {components: {'my-component-1': MyComponent1,'my-component-2': MyComponent2,'my-component-3': MyComponent3}
}
</script>

在上述代码中,我们同时注册了三个局部组件,并分别指定了它们的名称。这样,在模板中就可以使用 < my-component-1 >、< my-component-2 > 和 < my-component-3 > 这三个标签来调用这三个组件了。

课程

案例:

  <div id="app"><home-nav></home-nav><product-item></product-item></div><template id="product"><div class="product"><h2>{{title}}</h2><p>商品描述, 限时折扣, 赶紧抢购</p><p>价格: {{price}}</p><button>收藏</button></div></template><template id="nav"><div>-------------------- nav start ---------------</div><product-item></product-item><div>-------------------- nav end ---------------</div></template>
 // 1.创建appconst ProductItem = {template: "#product",data() {return {title: "我是product的title",price: 9.9}}}// 1.1.组件打算在哪里被使用const app = Vue.createApp({// components: option apicomponents: {ProductItem,HomeNav: {template: "#nav",components: {ProductItem}}},// data: option apidata() {return {message: "Hello Vue"}}})// 2.挂载appapp.mount("#app")

图解:
在这里插入图片描述

案例:
在这里插入图片描述在这里插入图片描述

组件间的嵌套

在这里插入图片描述

(3) 组件名字

两种方式

  1. - 连接
  2. 使用驼峰标识符

组件名称使用驼峰式时,components:{ MyItem } ,在 <template></template> 内使用时,以下两种方法都可以调用
<MyItem></MyItem> <my-item></my-item>

6.2. 通信

父组件传递给子组件: 通过 props 属性
子组件传递给父组件: 通过 $emit 触发事件
在这里插入图片描述

(1) 父传子 子用 props 接收 ⭐

子组件放props:[ ],props 位置

 // vue3export default{components: { },    // 局部组件data() { return { }  },  // 数据methods:{ },     // 方法computed: { },    // 复杂数据处理watch:{ },     // 监听props:[] // 或{}    //接收父组件传递过来的属性}

根(父)组件
(app.vue)

<!-- 没加:的,传的是字符串类型        加了:的,会变为js代码,自动变成数字类型 --><show-info name="why" :age="18" :height="1.88" address="广州市" abc="cba" class="active" /><show-info name="kobe" :age="30" :height="1.87" />

子vue

script用下面的语法接收,接收后template{{}}调用

  • props 数组语法props: ["name", "age", "height"]

    弊端: 1、不能对类型进行验证;2、没有默认值的

  • props 对象语法

  export default {props: {name: {type: String,default: "我是默认name"},age: {type: Number,required: true,default: 0},height: {type: Number,default: 2},// 重要的原则: 对象类型写默认值时, 需要编写default的函数, 函数返回默认值friend: {type: Object,default() {   // 或 dafault: () => ({ name: "james" })return { name: "james" }}},hobbies: {type: Array,default: () => ["篮球", "rap", "唱跳"]},showMessage: {type: String,default: "我是showMessage"}}}

(2) 子传父 子用 $emit 发送⭐

在这里插入图片描述

添加emits属性,方便查看发送的参数名,且父组件编写时会自动提示

export default {// 1.emits数组语法emits: ["add"],methods: {}}

全貌:
在这里插入图片描述
关于emits验证语法,(先执行函数,再验证,即使为false也是先执行函数,仅为提醒作用)
在这里插入图片描述

(3) 非Prop的Attribute

官方概念

未传递的属性
在这里插入图片描述

解释

用props演示来解释

父组件有传 address="广州市" abc="cba" class="active" (👆①)
子组件没有{{address}}接收语句,但vue会自动帮我们接收,添加到子组件的根元素上(在浏览器页面代码上
在这里插入图片描述

inheritAttrs属性决定要不要自动接收

export default {inheritAttrs: false,  // 不接收props: {}}

第一种情况
设置了inheritAttrs: false,但又想在某元素上调用,则设置$attrs
:class="$attrs.class"

第二种情况
不设置inheritAttrs(就是要接收),又有多个根(如下图同级的div)的情况下,使用v-bind="$attrs",告诉浏览器把属性传到哪个根(div)上

在这里插入图片描述

(4) 通信案例(一)

chatgpt提供案例,简单的 父传子+子传父

20230529061837

(5) 通信案例(二)

课程提供的综合案例

在这里插入图片描述

代码:
在这里插入图片描述

代码分析:
在这里插入图片描述

default:()=>[] 默认值:返回空数组

(6) 非父子通信 事件总线 ⭐

provide  Inject

最上层父组件有一个 provide 选项来提供数据
(孙)子组件有一个 inject 选项来开始使用这些数据
在这里插入图片描述

provide提供数据

import { computed } from 'vue'   // 有@click= 事件// provide一般都是写成函数provide() {return {name: "why",age: 18,message: computed(() => this.message) // computed 复杂数据处理,数据发生变化时自动更新}           // this.message 数据来自于data}

Inject 使用数据

inject: ["name", "age", "message"]

全局事件总线

跨层级较多时采用事件总线方法
在这里插入图片描述

vue2 有事件总线,vue3移除了,官方推荐了mitt和tiny-emitter库,这里我们使用hy-event-store

安装库npm install hy-event-store,里面有提供HYEventBusHYEventStore

创建全局总线,utils > event-bus.js

import { HYEventBus } from 'hy-event-store'const eventBus = new HYEventBus()export default eventBus

A 发出事件eventBus.emit("名称", 数据, 数据)
B 监听事件created() { eventBus.on("名称", (数据名, 数据名) => { } ) }

created() { eventBus.on("名称", (数据名, 数据名) => {  })
}

A.vue

  import eventBus from './utils/event-bus'export default {methods: {bannerBtnClick() {console.log("bannerBtnClick")eventBus.emit("whyEvent", "why", 18, 1.88)}}}

B.vue

 import eventBus from './utils/event-bus'export default {created() {           //声明周期函数eventBus.on("whyEvent",(name, age, height)=>{console.log("whyEvent事件在app中监听", name, age, height)this.message = `name:${name}, age:${age}, height:${height}`  // 更改data的message数据})}}

监听后一般要做移除工作,以C.vue文件,监听总线为例

  import eventBus from './utils/event-bus'export default {methods: {whyEventHandler() {console.log("whyEvent在category中监听")}},created() {            // 声明周期函数eventBus.on("whyEvent", this.whyEventHandler)   // 监听的是whyEvent,变化时执行方法},unmounted() {console.log("category unmounted") eventBus.off("whyEvent", this.whyEventHandler)  // 销毁监听}}

6.3. 插槽

定义

  • 抽取共性,预留不同

  • 将共同元素、内容依然在组件内进行封装

  • 不同的元素使用slot作为占位,让外部决定显示什么元素

(1) 基础用法(单插槽)

<slot></slot>
在这里插入图片描述

(2) 具名插槽(多插槽)

子组件传递:<slot name="插槽名称">默认值</slot>"

父组件接收,方式一:v-slot:插槽名称
      方式二:#插槽名称

(父组件未指定接收名字时,名称为default 。v-slot:default

子组件

 <div class="right"><slot name="right">right</slot> </div>

里面的right为默认内容,当父组件调用又无实际内容时,显示right

父组件

  <template #center><span>内容</span></template><template v-slot:right><a href="#">登录</a></template>

在这里插入图片描述

(3) 动态插槽

在这里插入图片描述

(4) 作用域插槽

即使有通讯,vue - template - {{ }}也只是获取自己的 vue-script-data,这称之为渲染作用域

改良目的:不想制作文字使用,也可以是button
在这里插入图片描述
更多:
在这里插入图片描述
ppt内的解释:
在这里插入图片描述

6.4. 生命周期

速览

创建 -> 加载(挂载) -> 更新 -> 销毁(卸载)

/–创建前 beforeCreate
/–创建后 created

/–加载前 beforeMount
/–加载后 mounted      DOM渲染在此周期中已经完成

/–更新前 beforeUpdate
/–更新后 updated

/–销毁(卸载)前 beforeDestroy / beforeUnmount
/–销毁(卸载)后 destroyed / Unmounted

销毁和卸载的区别

  1. vue版本
    vue2 -> 销毁 destroyed
    vue3 -> 卸载 Unmounted

  2. 处理工作上 —— chatgpt
    destroyed适合处理一些清理工作,如清除计时器、取消网络请求、销毁第三方库等
    Unmounted适合做一些操作DOM的工作,如获取元素高度、保存滚动位置等

  3. 执行时机 —— chatgpt
    destroyed:组件实例完全销毁之后调用,此时所有的指令以及事件监听器都已经被移除,数据绑定也被解绑。
    Unmounted:组件从DOM中卸载之前调用,此时可以访问到组件实例、指令、事件以及DOM元素,但是该组件的实例上的所有指令和事件监听器都已经被移除。

生命周期详解

生命周期函数是一些钩子函数(回调函数)。P1027
请添加图片描述  在这里插入图片描述  在这里插入图片描述

  export default {// 1.组件被创建之前beforeCreate() {console.log("beforeCreate - 组件被创建之前");},// 2.组件被创建完成 ⭐created() {console.log("created - 组件被创建完成")console.log("1.发送网络请求, 请求数据")console.log("2.监听eventbus事件")console.log("3.监听watch数据")},// 3.组件template准备被挂载beforeMount() {console.log("beforeMount - 组件template准备被挂载")},// 4.组件template被挂载: 虚拟DOM -> 真实DOM ⭐mounted() {console.log("mounted - 组件template被挂载")console.log("1.获取DOM")console.log("2.使用DOM")},// 5.数据发生改变// 5.1. 准备更新DOMbeforeUpdate() {console.log("数据发生改变");console.log("beforeUpdate - 准备更新DOM")},// 5.2. 更新DOMupdated() {console.log("updated - 更新DOM")},// 6.卸载VNode -> DOM元素// 6.1.卸载之前beforeUnmount() {console.log("beforeUnmount - 卸载之前")},// 6.2.DOM元素被卸载完成 ⭐unmounted() {console.log("unmounted - DOM元素被卸载完成")}}

移除案例

20230531065944

6.5. ref引用 $refs

ref是什么?
在Vue中,ref是一种特殊的属性,用于给元素或组件指定一个唯一的标识符,以便可以在JavaScript代码中访问该元素或组件。通过this.$refs对象,我们可以访问所有具有 ref 属性的元素或组件,并且可以执行操作,例如访问DOM元素的属性或调用组件的方法。

作用
帮助获取DOM

在Vue开发中我们是不推荐进行原生DOM操作的;
这个时候,我们可以给元素或者组件绑定一个ref的attribute属性

使用方法
html元素加上ref="名称",方法 methods 中通过 this.$名称 获取

<h2 ref="title" class="title" :style="{ color: titleColor }">{{ message }}</h2>
<button ref="btn" @click="changeTitle">修改title</button><banner ref="banner"/>
  export default {components: { Banner  },  data() { return {  message: "Hello World", titleColor: "red"  }  },methods: {changeTitle() {// 2.获取h2/button元素console.log(this.$refs.title)  // 打印整个h2代码console.log(this.$refs.btn)   // 打印整个button代码// 3.获取banner组件: 组件实例console.log(this.$refs.banner)  //  打印代理对象// 3.1.在父组件中可以主动的调用子组件的对象方法this.$refs.banner.bannerClick()  // 执行方法// 3.2.获取banner组件实例, 获取banner中的元素console.log(this.$refs.banner.$el) // 打印banner的<div>...</div>// 3.3.如果banner template是多个根, 拿到的是第一个node节点// 注意: 开发中不推荐一个组件的template中有多个根元素// console.log(this.$refs.banner.$el.nextElementSibling)// 4.组件实例还有两个属性(了解):console.log(this.$parent) // 获取父组件  打印代理对象console.log(this.$root) // 获取根组件   打印代理对象}}}

效果
20230601021942

6.6. 动态组件 :is="组件名称"

通过 <component :is="组件名称"> </component> 绑定

效果
20230601031744

解释
组件名Home和home的大小写没关系

在这里插入图片描述
里边的props + $emit 知识点
在这里插入图片描述

6.7. keep-alive 保持存活

如:tabA有计数器,选择了10。此时切换到tabB,再切换回tabA,我的计数器是多少?

  • 用了 <keep-alive></keep-alive> ,计数器仍保持在10的状态
  • 没有 <keep-alive></keep-alive> ,计数器还原(原因:切换到tabB时,tabA已经被卸载,使用unmounted 可看到提示已被卸载)

属性:

  • include 属性决定哪个要保持存活,不被销毁,(字符串、正则、数组)
  • exclude 属性决定哪个不被缓存,要销毁,(字符串、正则、数组)
  • max 属性决定最多可以缓存多少组件实例,一旦达到这个数字,那么最近没有被访问的实例会被销毁,(数字、字符串)
  • 注意组件name中间,后不加空格,直接写组件名称
<keep-alive include="组件A定义的name,组件B定义的name"><component :is="组件名称"></component>
</keep-alive>

在这里插入图片描述

created 创建后、unmounted 卸载后(卸载成功)

当使用了keep-alive保持存活后,该组件就不会执行unmounted函数

缓存组件的生命周期

// 对于保持keep-alive组件, 监听有没有进行切换
// keep-alive组件进入活跃状态
activated() {console.log("home activated")      // 进入该组件
},
deactivated() {console.log("home deactivated")    // 退出该组件
}

7. 组件(二)

7.1. 异步组件 (了解)

异步组件不常用,一般使用懒加载的方式

学习异步组件前,先了解一下webpack 代码分包

(1) webpack 代码分包

在这里插入图片描述

当自己编写的页面代码过多时,首屏渲染速度就会延长。

(2) 异步组件使用

打包某个js文件
20230601063542

打包某个组件

方式一:

// 引入方法
import { defineAsyncComponent } from 'vue'
// 引入组件
const AsyncCategory = defineAsyncComponent(() => import("./views/Category.vue"))
// 在components中调用
export default {components: {Category: AsyncCategory},
}// 注意:
// 是把 import Category from './views/Category.vue' 改为上面defineAsyncComponent方法

方式二:
20230601064300

7.2. 组件的v-model

普通v-model

<!-- 1.input v-model -->
<input v-model="message">
<input :value="message" @input="message = $event.target.value">

概念

顾名思义,在组件中使用v-model,它默认完成了两件事情:

  1. v-bind:value的数据绑定,其中modelValue 是默认名
  2. @input的事件绑定,@update:model-value 也是事件默认名v-on:update:model-value = @update:model-value

基本用法

上下两行是等价的,(modelValue 等同于 model-value)

<my-input v-model="message"/>
<my-input :model-value="message" @update:model-value="message = $event"></my-input>

APP.vue

<template><div class="app"><counter v-model="appCounter"></counter><counter :modelValue="appCounter" @update:modelValue="appCounter = $event"></counter></div>
</template><script>import Counter from './Counter.vue'export default {components: {Counter},data() {return {appCounter: 100,}}}
</script><style scoped>
</style>
  1. 上下两个counter是相等的
  2. 绑定更新事件,$event数据等于自己绑定的值

Counter.vue

<template><div><h2>Counter: {{ modelValue }}</h2><button @click="changeCounter">修改counter</button></div>
</template><script>export default {// 接收props: {modelValue: {type: Number,default: 0}},// 发送emits: ["update:modelValue"],methods: {changeCounter() {this.$emit("update:modelValue", 999)} }}
</script><style scoped>
</style>
  1. 接收modelValue为数字类型,如无内容默认为0

效果:点击前100,点击后为999

组件自定义名称

<!-- 3.组件的v-model: 自定义名称counter -->
<counter2 v-model:counter="appCounter" v-model:why="appWhy"></counter2>

App.vue

<template><div class="app"><counter2 v-model:counter="appCounter"v-model:why="appWhy"></counter2></div>
</template><script>import Counter2 from './Counter2.vue'export default {components: {Counter2},data() {return {appCounter: 100,appWhy: "coderwhy"}}}
</script><style scoped>
</style>

counter2.vue

<template>
<div><h2>Counter: {{ counter }}</h2><button @click="changeCounter">修改counter</button><!-- why绑定 --><hr><h2>why: {{ why }}</h2><button @click="changeWhy">修改why的值</button>
</div>
</template><script>
export default {props: {// 接收counter组件v-model,默认值为数字0,有接收值,为100counter: {type: Number,default: 0},// 接收why组件v-model,默认值为空字符串,有接收值,为coderwhywhy: {type: String,default: ""}},// 发送emits: ["update:counter", "update:why"],methods: {changeCounter() {this.$emit("update:counter", 999)},changeWhy() {this.$emit("update:why", "kobe")}}
}
</script><style scoped>
</style>

7.3. 混入Mixin

组件和组件之间有时候会存在相同的代码逻辑,我们希望对相同的代码逻辑进行抽取,vue2和vue都支持使用Mixin来完成(vue2使用较多,vue3已经不怎么用了)

作用

  1. 分发Vue组件中的可复用功能
  2. 一个min对象可以包含任何组件选项
  3. 当组件使用Mixin对象时,所有Mixin对象的选项将被 混合 进入该组件本身的选项中

使用方法

  1. views文件夹同级新建一个mixins文件夹

20230619153407

  1. 新建message-mixin.js文件
export default {data() {return {message: "Hello World"}},created() {console.log("message:", this.message)}
}
  1. About.vue引用文件
<template><h2>About组件</h2>
</template><script>// 引入方法import messageMixin from '../mixins/message-mixin'export default {// 调用mixins: [messageMixin]}
</script><style scoped>
</style>

混入的含义

message-mixin.js的data数据会自动合并(混入)到About.vue的data里,包括生命周期(created)等其他函数都会自动合并(混入)并执行

合并规则

Mixin对象中的选项和组件对象中的选项发生冲突分成不同情况来处理

情况一:如果是data函数的返回值对象

  • 返回值对象默认情况下会进行合并
  • data返回值对象的属性发生冲突时保留组件自身的数据
    (如message-mixin.jsAbout.vue都有message,则以About.vue的message数据为准)

情况二:如果有相同的生命周期钩子函数

  • 生命周期的钩子函数会被合并到数组中,都会被调用

情况三:值为对象的选项,例如methodscomponentsdirectives,将被合并为同一个对象

  • 比如都有methods选项,并且都定义的方法,那么它们都会生效
  • 但如果对象的key相同(key:value),那么会取组件自身的对象键值(如上案例,假设message-mixin.jsAbout.vue都有massage,则优先取About.vue的值)

全局混入
app.mixin({})

8. 路 由

9. 搭建项目

主要有以下几种方法

  1. CDN引入方式:这种方法适用于简单的页面,只需要在HTML文件中引入Vue的CDN链接即可。这种方式在Vue 2和Vue 3中都适用。

  2. Vue CLI 脚手架:Vue CLI是Vue官方提供的脚手架工具,可以快速搭建起一个Vue项目,并且提供了一些便捷的功能,如自动生成代码打包压缩等。在Vue 2中,通过命令行输入“vue create 项目名”即可创建项目;而在Vue 3中,使用Vue CLI需要先全局安装Vue CLI 4.x或以上版本,然后通过命令行输入“vue create 项目名”创建项目。

  3. 使用Webpack手动配置:如果想要更加灵活地配置项目,可以选择手动配置Webpack。在Vue 2中,需要先安装Vue Loader和相关插件,然后通过Webpack配置文件进行相关配置;而在Vue 3中,Vue Loader已经与Vue CLI集成,只需要在Webpack配置文件中引入Vue即可开始开发。

  4. 使用Vite:Vite是Vue3官方推荐的构建工具,可以快速搭建Vue项目,并且具备高效的开发体验。Vite使用ES模块化机制来加载代码,能够极大地提升项目的启动速度和开发效率。使用Vite搭建Vue项目非常简单,只需要全局安装Vite并执行“vite create 项目名”即可创建项目。

9.1. Vue CLI 脚手架

(1) 安装

  1. 安装   npm install @vue-cli -g 不行时用 npm install -g @vue/cli
  2. 查看版本 vue --version

(2) 创建项目 —— vue create 项目名称

  1. vue create 项目名称,(底层打包工具是 webpack)

  2. 是否使用淘宝源 >> no
    在这里插入图片描述

  3. 选择预设 >> Manually select features 手动选择新特性
    在这里插入图片描述

  4. 后期做项目会选择:
    在这里插入图片描述

  5. 选择vue版本 >> 3.x
    在这里插入图片描述

  6. babel放独立文件还是package.json
    在这里插入图片描述

  7. 是否生成预设 >> yes >> 名称
    在这里插入图片描述

  8. 使用工具 >> NPM 或者 PNPM
    在这里插入图片描述

  9. 项目创建成功
    在这里插入图片描述

  10. 启动服务 npm run server ,更改了配置文件得重新启动服务

  11. 打开项目 http://localhost:8080/

(3) 创建项目 —— npm init vue@latest

(底层打包工具是vite,越来越流行,打包效率高?待核实

  • 是否安装create-vue,y
  • 项目名称
  • typeScript,n
  • jsx,n
  • 暂时都n
  • 创建成功👇
    在这里插入图片描述

(4) 区别

(来自chatgdp)

npm init vue@latestvue create 项目名称都是用于创建Vue.js项目的命令,但它们有以下不同点:

  1. npm init vue@latest是通过npm包管理器在当前目录下初始化Vue.js项目,并生成一个package.json文件。
    vue create 项目名称则是通过Vue CLI工具在指定目录下创建新的Vue.js项目。

  2. npm init vue@latest只会安装Vue.js框架本身,你需要手动安装其他依赖项,如Vue Router、Vuex等。
    vue create 项目名称可以快速构建带有预设置集成的Vue.js应用程序,包括路由、状态管理、Linter、测试等等。它还提供了各种选项和预设模板,方便你根据需求进行选择。

因此
如果你想要更加自定义化地创建Vue.js项目,则可以使用npm init vue@latest
如果你想要更快捷地创建符合标准规范的Vue.js应用程序,则可以选择vue create 项目名称

9.2. 项目结构

在这里插入图片描述

(1) .browserlistrc 浏览器视配文件

/–>1% 市场占有率大于1%
last 2 versions 支持最后两个版本
not dead 还在维护的
not ie 11 非ie11

(2) 配置别名

vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,configureWebpack: {resolve: {// 配置路径别名// @是已经配置好的路径别名: 对应的是src路径alias: {"utils": "@/utils" }}}})

使用 utils/文件夹/文件夹/math.js 时,可以直接写 utils/math,(此时没有文件路径提示)

添加文件路径提示:jsconfig.json

"paths": {"utils/*": ["src/utils/*"]
},

解释 jsconfig.json
在这里插入图片描述

(3) vscode 关于 vue 的插件

vetur:推荐在 vue2 使用            ✗
volar:推荐在vue3使用,目前比较好用     ⭐

(4) style 作用域

  • 在 app.vue 内设置的常见样式.title { },会使底下其他组件的.title { }生效,为不影响他们,添加<style scoped></style> 生成自己的作用域

10. 概念

10.1. VNode

  1. 全程Virtual Node,虚拟节点
  2. 无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode
  3. VNode的本质是一个JavaScript的对象
  4. template 👉 VNode 👉 真实DOM

10.2. 虚拟DOM

20230516045514

  1. 由多个虚拟节点(VNode)组成一颗树(虚拟Dom)
  2. 为什么要生成虚拟DOM?
    • 便于跨平台,写一份代码渲染到多个平台上
      20230516050157

10.3. proxy

proxy 代理对象,解释:《一篇彻底理解Proxy - LBJ》

10.4. el:#app

意思是把前面html页面中id='app进行了染

10.5. $event

20230612084533

10.6. 解构

(待补充)

10.7. 单项数据流

数据传递给另一个组件时,只允许阅读,不允许修改,称为单向数据流

11. 案例

11.1. 购物车

效果
在这里插入图片描述

代码截图
在这里插入图片描述 在这里插入图片描述

分析

  1. books,数据来源:本地和服务器写法
    在这里插入图片描述

created(){} 生命周期回调函数:

当前的组件被创建时自动执行一般在该函数中, 会进行网络请求

  1. 总价的两种计算方法,totalPrice() {} ,有高阶函数写法 reduce 累加器
    在这里插入图片描述

该高阶仅有一句话,可省略为
在这里插入图片描述

20230521232409

  1. 有多个价格在不同的位置,但前面都要添加¥,可以通过设置一个方法的形式添加
    在这里插入图片描述
  2. 监听点击了哪一个商品的 + / -
    在这里插入图片描述
  3. 小于等于1时,按钮禁用
    在这里插入图片描述
  4. 移除商品
    在这里插入图片描述
  5. 购物车移除全部商品后,文字提示
    在这里插入图片描述
  6. 点击效果,点击行变色,其他行还原
    在这里插入图片描述

11.2. 切换+通信

看 5.2 (5) 通信案例(二)课程提供的综合案例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/6906.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小红书内容传播之品牌推广,干货分析

对于一个品牌来说&#xff0c;想要做好信息传播&#xff0c;迅速抢占市场&#xff0c;找准战场非常重要。而小红书&#xff0c;很显然就是时下众多品牌&#xff0c;竞相进驻的平台。那么如何在小红书平台做好品牌推广呢&#xff0c;今天为大家解读下。 一、做好品牌推广的三大步…

小程序 分享卡片 禁止个人及群聊二次转发

遇到开发需求说&#xff0c;分享消息给好友及群聊&#xff0c;但不允许二次转发 查了好多资料 最后很简单的解决了 就是在onShareAppMessage方法中 加uni.showShareMenu和 wx.updateShareMenu这两个方法 就可以实现需求 onShareAppMessage(){var that this;uni.showShareM…

小红书APP群控实战

设备清单 魅蓝Note5 4台 sim卡 4张 USB连接线TypeC 4根 优越者(UNITEK)USB分线器带独立电源 1台 PC i5 8g内存 1台 无线路由器 1台 硬件环境 实景 软件环境 魅蓝NOTE5 Xposed Installer 3.1.5 Xposed Version 89 Just Trust Me 0.2 小红书APP 6.8 使用手机号注册小红书账号…

小红书数据平台:笔记爆文率提升的三大秘诀公式!

导语 对于小红书商家 / 博主来说&#xff0c;写出爆文就像买彩票&#xff0c;根本不能预知哪一篇会爆。2023年&#xff0c;小红书哪些内容会脱颖而出呢&#xff1f;我们又该如何把握热点趋势&#xff0c;实现优质内容转化出爆文~ 美妆作为小红书的长红赛道&#xff0c;本文我…

详解小红书引流小技巧和矩阵玩法

众所周知&#xff0c;在各大公共领域平台中&#xff0c;小红书这个平台可以说是最受品牌商家关注的战场之一。而且作为一个种草平台&#xff0c;相比其他平台&#xff0c;小红书用户搜索的目的性更加精准&#xff0c;那么我们如何才能将公域流量引导到个人领域并获得准确的流量…

3.27 分享两个在PC上浏览小红书内容的方法【玩赚小红书】

第一个&#xff1a;微信小程序 可以通过登录电脑端的微信&#xff0c;在微信里面搜索小红书小程序使用&#xff0c;同时也可以添加到桌面。 ​ ​ ​ 方法二&#xff1a;通过搜索引擎跳转 给大家分享一下如何在浏览器中搜索&#xff0c;&#xff08;以bing搜索引擎为例&…

小红书运营方案:从0到1的引流微信私域指南

前全国午和小伙伴在沟通,她做的是小红书旅游类项目,目的是用户流量,转微信私域。她面临的疑问,不止是小红书营销传播,还要打磨服务产物。 她自己做过几年运营,思路相对清晰,我也给她罗列了7个框架;后面在复盘时,我又增加了3个点。 她这个项目属于旅游类,偏向亲子旅…

抖音卡片/快手/小红书/h5浏览器/微博跳转微信/qq/微信公众号/指定链接

首先说明&#xff0c;本文内容及教程均转载自&#xff1a;抖音私信卡片系统源码搭建【图文教程】已经本人允许 功能说明&#xff1a; 抖音卡片跳转 微信 抖音卡片跳转 qq 抖音卡片跳转 微信公众号 抖音卡片跳转 指定网页链接 快手跳转 微信 快手跳转 qq 快手跳转 微信公众号 …

投放指南|小红书投放被限流,品牌该如何是好

前言 每当双十一来临&#xff0c;很多品牌就已经开始在九、十月加大小红书平台的投放笔记量&#xff0c;为双十一收割做准备。当然也有不少品牌和博主反应&#xff0c;这段时间有不少笔记出现了限流情况&#xff1a;互动量上不去&#xff0c;小眼睛数量也比之前少了非常多&…

小红书 程序员七夕礼物 - 微信每日早安推送 简单部署一键启动

更新&#xff1a;仓库wechat-push中介绍一种无需服务器的部署方式&#xff0c;注册使用码云流水线自动发送消息。没有服务器不会编程的小伙伴&#xff0c;推荐大家使用这种方式。 本文来自 小红书大佬七夕节的礼物 原版大佬的代码&#xff0c;配置有些分散&#xff0c;我将其统…

群发猫——外贸全社媒平台官方接口群发不封号

当你还没有开始的的时候别人已经开始了&#xff0c;当你在犹豫的时候别人已经走在成功的路上了&#xff0c;当你在决策的时候别人已经上岸了&#xff0c;当你还在考虑的时候 别人已经成功&#xff0c;走在时代前言的人&#xff0c;慢一步 &#xff0c;步步就慢&#xff0c;跟不…

基于vite4+pinia2模仿chatgpt移动端聊天模板Vue3MobileGPT

运用vite4.x构建mobile端仿chatgpt聊天实例Vue3-mobileGPT vue3-mobilegpt 基于 vite4vue3pinia2vue-routervant 等技术开发移动端仿ChatGPT智能聊天项目模板。支持lightdark两种主题&#xff0c;搭配vue3组件库Vant&#xff0c;界面简洁美观。 就前几天OpenAI就推出了IOS版Cha…

ChatGPT模板设计领取

含登录系统&#xff0c;数据库系统&#xff0c;后端系统&#xff0c;卡密系统&#xff0c;宣传系统。对接GPT3.5模型&#xff0c;API接口&#xff0c;服务器对接&#xff0c;标准UI设计&#xff0c;标准前端设计。 模板&#xff1a;chat.stellar.hk

让我们一起看看chatGPT的CSS代码水平

近日&#xff0c;chatGPT火爆了&#xff0c;我也看到了许多人说 人工智能 对前端的影响&#xff0c;在GPT-4发布时&#xff0c;也展示了GPT的代码能力–10秒钟根据图片上网页布局生成前端网页&#xff0c;也是非常强大了。在好奇心驱使下&#xff0c;我试了试chatGPT写前端CSS代…

作为一名前端开发,我们可以让chatGPT帮我们做什么?

您可以利用 ChatGPT 来帮助您完成以下任务&#xff1a; 自动生成代码注释&#xff1a; ChatGPT 可以根据您提供的代码片段生成对应的注释&#xff0c;帮助您解释代码的功能和实现细节。 /*** 计算两个数字的和* param {number} a 第一个数字* param {number} b 第二个数字* re…

6月城市之星领跑活动获奖名单已出炉

经过一个月的角逐&#xff0c;6月城市之星领跑活动上榜名单终于出炉啦&#xff0c;本次城市赛道是根据最后登陆且6月份有入围博客之星用户的城市一共368个城市&#xff0c;城市人数划分区间具体情况如下&#xff1a; 200以上城市2个&#xff0c;其中有一些博主的城市由于未获取…

刘慈欣谈 ChatGPT:我的看法,跟大家有些不一样...

作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 著名科幻作家刘慈欣在近日联合国大会上做演讲&#xff0c;他谈到了以 ChatGPT 为代表的人工智能可能会给人们带来的影响。 以下是大刘的主要观点&#xff1a; 首先&#xff0c;最明显的一点是人工智能…

雷军首谈ChatGPT:要么成为AI的主人,要么被AI淘汰!

作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 小米创始人雷军&#xff0c;最近在谈到ChatGPT时表示&#xff1a;“AI给人类带来的影响&#xff0c;远超以往任何一次技术革命。许多行业将被颠覆&#xff0c;一大批旧的岗位被AI取代&#xff0c;同时…

(大集合)AI工具和用法汇总—集合的集合

AI 工具和用法汇总 汇集整理 by Staok/瞰百&#xff0c;源于相关资料在我这慢慢越积累越多&#xff0c;到了不得不梳理的程度。 文中有许多内容作者还没有亲自尝试&#xff0c;所以很多内容只是罗列&#xff0c;但信息大源都已给出&#xff0c;授人以渔&#xff0c;欢迎 PR 补…

“国货之光”文心一言正式发布:百度大语言模型背后那些不为人知的细节

文章目录 前言一、文心一言是什么二、发布会内容1.文学创作2.商业文案创作3.数理逻辑推算4.中文理解5.多模态生成 三、文心一言那些不为人知的细节总结 前言 去年年末&#xff0c;ChatGPT以惊人的速度成为焦点&#xff0c;激起了中国科技界和创投领域的热情&#xff0c;吸引了…