目录
安装插件,将两个script变成一个script,其中一个用来配置组件名的script删掉,将组件名放到另一个script中定义
情况四:监视ref或者reactive定义的对象的某一个属性
24.5.15:
创建Vue3工程
1.确定自己电脑有没有nodejs环境,在cmd中输入node,如果出现Node.js的版本号说明已经有这个环境了,否则搜索Node.js安装
2.先在D盘创建一个文件夹Vue3_Study,然后在这个空文件夹中右键选择终端打开。
3.输入npm create vue@latest
4.输入项目名称 hello_vue3,然后下面的选项除了第一个选择是,其余的选择否
5.在VSCode中打开hello_vue3
编写App组件
1.index.html是项目的入口文件
2.加载index.html文件之后,解析<script type="module" src="/src/main.ts"></script>跳转到src="/src/main.ts"
3.Vue3通过createApp函数创建一个应用实例
4.
编写Person.vue
1.在src文件夹中新建一个components文件夹用来存放自己实现的效果
2.在components文件夹中新建一个Person.vue文件
3.仿照App.vue写Person.vue
<template>
<div class="person">
<h2>姓名:{{ name }}</h2>
<h2>年龄: {{ age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="showTel">查看联系方式</button>
</div>
</template>
<script lang="ts">
export default{
name:'Person',//组件名
data(){
return {
name:'张三',
age:18,
tel:'17999999999'
}
},
methods:{
showTel(){
alert(this.tel)
},
changeName(){
this.name='罗xx'
},
changeAge(){
this.age+=1
},
}
}
</script>
<style scoped>
.person{
background-color: aqua;
box-shadow: 0 0 10px;
padding: 20px;
}
button {/*修改按钮之间的间隔 */
margin: 0 5px;
}
</style>
4.在App.vue中引入这个Person效果
5.最后效果:
5.下载vue插件便于在网页中检查
5.1网页搜索极简插件
5.2搜索vue
5.3选择推荐下载
5.4下载完之后将压缩包解压到某个位置
5.5打开常用浏览器,点击右上角的三个点,选择扩展-管理扩展,然后将开发者模式打开
5.6将压缩之后的文件夹拖入到浏览器页面选择安装
5.7之后在使用检查就可以看到VUE插件(没有的话看一下那个更多工具)
24.5.16:
OptionsAPI与CompositionAPI的区别
vue2--OptionsAPI选项式
vue3--CompositionAPI组合式
setup概述:
vue2中写数据、写方法:
vue3:使用setup
setup函数也可以返回一个函数:
如
return function(){
return '哈哈'
}
等同于箭头函数
return () => '哈哈'
这时候页面都只会有哈哈两个字
setup与OptionsAPI
原始的写法data和methods可以和setup并存,并且在data中可以用this.变量名读取到setup中的变量,methods中同理(因为setup在data和methods之前执行,当执行到data和methods时,setup中的数据都准备好了)。
但是setup中是不能读取到data和methods中的变量的,当执行setup时,data和methods还没执行呢,哪来的数据给我读呢
setup的简便写法:
这是刚刚学的setup的写法,不方便的地方就是要将setup函数中的变量、方法返回才能使用
接下来学习另一种写法,可以自动返回:
两个script,一个配置组件名字,一个配置组合式API,export标红是因为default和{之间没有打空格。。。。
安装插件,将两个script变成一个script,其中一个用来配置组件名的script删掉,将组件名放到另一个script中定义
1.终端输入: npm i vite-plugin-vue-setup-extend -D 实现下载插件
2.配置vite.config.ts:
之后代码就可以变成最简单的形式:
24.5.18:
用ref创建基本类型的响应式数据
首先定义一个数据时,如果想让他是响应式数据(能显示变化)的,就用ref包一下:
let xx = ref(初始值)
并且在方法中使用这个变量时不能再直接引用变量名,而要变量名.value
用ref实际上是把数据变成了一个RefImpl对象
用reactive创建对象类型的响应式变量:
只需要用reactive包裹,而引用时不需要做什么变化
用reactive实际上是把数据变成一个proxy对象
这是普通的对象的写法,当按下按钮,price虽然变了但是不会显示到页面
这是用reactive之后的,car对象变成响应式的了:
对象是数组时:
没有用reactive时:
v-for g in game代表的意思:遍历game数组的每一个数据放到形参g中
v-bind:key="g.id"代表的意思:数组中的每一条数据的唯一标识
使用reactive:
reactive处理的数据是深层次的
这种情况下也能a,b,c,d都是响应式的
用ref创建对象类型的响应式变量:
只要是用ref创建的响应式数据,都要在摸到数据时立马.value
配置插件,使得自动添加.value
查看已安装扩展,点击vue-Official右下角的设置图标,选择扩展设置,将Auto Insert:Dot Value勾选。这样的话只需要正常引用变量,不需要关注它是ref的还是reactive的,不用关心要不要加value,如果需要加的话,会自动补全
reactive和ref的对比
1.reactive重新分配一个新对象,会失去响应式
我希望这个对象的每一个属性都变化该怎么做呢,可以一个一个属性改,但是会不方便
所以使用reactive要对对象的多个属性进行修改时可以用Object的assign函数
Object.assign(obj1,obj'2,obj3)
意思是:将obj2的每一个属性加到obj1中,然后再将obj3的每一个属性加到obj1中
(同名的会被替换,本来没有的属性会被增加)
所以本例中可以这样写:
2.如果是ref创建的响应式数据,要修改对象中的属性,可以直接修改对象的value
注意:
reactive创建的响应式数据,执行Object.assign(Person,{name:'xx',age:18})实际上相当于批量的替换对象里的一些属性值,对象的地址是没有变的
ref创建的响应式数据,执行 Person.value = {name:'xx',age:18}相当于产生了一个新的对象,地址是变了的
toRefs与toRef
将reactive创建的响应式对象的东西解构拿出来并且使其仍然具备响应式能力
这种写法相当于重新定义了两个变量name和age,并且将reactive创建的响应式数据Person解构,将其属性值赋值给新定义的两个变量,但是这两个新定义的变量并不是响应式数据
所以可以通过toRefs,将reactive创建的响应式数据Person的每一个属性值赋值给新定义对象的同时将新定义对象变成ref响应式数据,而且自己定义的对象{name,age}与Person是有关联的,改变name.value也会改变Person.name,改变age.value也会改变Person.age,
所以页面显示的是name还是Person.name都是可以实现更改的
toRefs:可以将reactive创建的响应式对象中的每一个属性取出来
toRef:要指定你想取出来哪个属性:let nl = toRef (Person , 'age')
v-bind和v-model
v-bind:动态地绑定一个或多个特性,单项绑定,可以从数据流向页面,不能从页面流向数据,就是页面输入框的东西改了但是对应数据改不了,v-bind:属性=“”,可以简写成:属性=“”
这时候页面第一个输入框显示的内容就是firstName的值了,但是当你改变输入框中的文字,firstName的值却不会改变,即单向绑定
v-model:双向绑定,可以通过更新数据来实现页面的更新,也可以根据页面用户的修改而更新数据
computed计算属性:
实现读取第一个输入框中的姓和第二个输入框中的名拼装成全名
首先引入computed:imoort {computed} from 'vue'
上述方法写的fullName是一个只读属性,不能修改fullName的值,姓变化、名变化导致计算结果fullName发生变化,不叫作修改了fullName的值,要有代码fullName=。。。。才算修改了他的值
用get()函数得到计算之后fullName的值
set函数是出现了修改fullName的值的代码fullName=。。。。之后自动调用的,并且会得到。。。。这个参数,知道fullname最终想要修改得到的值,在set函数中处理使得页面显示对应的效果
watch监视数据
watch只能监视getter函数(有返回值的函数)、ref定义的数据、reactive定义的数据、包含上述内容的数组
情况一:监视ref定义的基本类型的数据
解除监视:可以通过console.log(stopWatch)知道它是一个函数
24.5.19
情况二:监视ref定义的对象类型的数据
以上代码,如果监视函数里面没有加上{deep:true}参数,也就是说没有开启深度监视,那么点击按钮1和按钮2,更改Person的属性,都不会执行watch函数,只有当Person的地址改变,即Person.value={name:'李四', age:'22'}时才会执行回调函数
开启了深度监视之后,当点击按钮1或者按钮2修改对象的属性时,会执行回调函数,打印新的值和旧的值,但是此时新的值和旧的值其实是一样的,都是更改之后页面显示的值,即新的值,因为对象地址没有变。如果点击的时按钮3更改整个对象,那么新的值和旧的值才会不一样
情况三:监视reactive定义的对象类型的数据
会默认开启深度监视,也就是说不用手动添加监视函数的第三个参数{deep:true},且即使{deep:false}也不能关上深度监视
reactive定义的响应式对象,不管是修改对象的某些属性还是执行Object.assign(Person ,{name:'李四', age:22})修改所有属性,实际上都是在原有对象上操作,不涉及新对象,不会发生地址的变化,所以新旧值都是一样的
情况四:监视ref或者reactive定义的对象的某一个属性
情况五:监视上述的多个数据
watchEffect:
watch:要指明监视源,想要监视几个就写几个
watchEffect:不需要指明监视源,直接使用,会自动分析你要监视的数据
首先还是要引入watchEffect
24.5.21
标签的ref属性:
这样的代码,如果点击按钮会打印什么内容呢?是‘北京’还是‘你好’?
会打印’你好‘
因为页面是从App组件开始渲染的,那么App.vue中的<h2 id="title2">你好</h2>会在Person组件之前渲染,那么自然也会在Person.vue的<h2 id="title2">北京</h2>之前渲染啦,所以当通过id=’title2‘找元素时会找到App.vue中的<h2 id="title2">你好</h2>
也就是说用id的话需要你各个组件之间的id都不能重复,否则就可能会出现问题
通过ref属性来代替id
这样的话两个title2是不一样的,在App.vue输出的就是App.vue里的title2,在Person.vue里输出的就是Person.vue的title2
注意的点:以上使用ref属性都是用在了html标签中,不能用在组件上
如果在App.vue中ref属性放到组件Person中:
而且Person.vue有一些数据
这样打印出来是一个实例对象,但是没有什么有用的东西,看不到Person.vue中的三个数据
相当于App.vue是父亲,Person.vue是儿子,儿子没有声明的话父亲是不能看他的隐私的,需要儿子自动说可以看我的哪些数据:
当然也可以少给App看几个数据,看需求
回顾ts中的接口、泛型、自定义类型
首先在src文件夹下创建一个文件夹types,用于存储对类型的规范
用这个接口来限制一个对象的属性标准,这样当你这个对象的属性名字与接口中写的不一样就会飘红:
要先引入你定义的这个接口规范
用这个接口来限制一个数组中所有对象的属性标准
方法1:利用泛型:定义一个变量,它是数组,且数组中的所有元素符合personInter规范
方法2:定义一个自定义类型规范
注意:
当想规范用reactive抱起来的数据时还是使用泛型
let personList = reactive<Persons>([
{id:'1',name:'张三',age:18},
{id:'2',name:'李四',age:19},
{id:'3',name:'王五',age:20}
])
props的使用
假设父组件App.vue要给子组件Person.vue传递一个数据,那么在父组件中通过<Person a="hh"/>可以传一个值为“hh”的a给Person组件
在Person.vue组件中肯定要接收这个数据才能使用,通过defineProps(['a'])接收
注意:就算只接收一个数据也要用数组;而且参数名字要相对应
之后在Person.vue组件中就可以直接通过a来得到’hh‘的值了
接收:
接收+保存:
接收props之后不能直接console.log(a)要先将接收的所有数据保存到一个对象中,通过对象.属性来获取
如果要传递一个对象:
加了冒号,也就是说用来v-bind,可以表明后面是js表达式,从而进行运算或者去找这个变量,如果没有加:,那么后面就是一个字符串,相当于传递了一个字符串过去
接收+限制类型+限制必要性+限制默认值
24.5.22
创建vue2项目:
在相应文件夹下面进入终端
vue create vue2_study
选择vue2
选择NPM
vue2的启动方式:
npm run serve(在package.json中的scripts中查看)
vue2的生命周期
四个阶段(创建、挂载(组件呈现到页面上)、更新、销毁)
八个钩子(生命周期函数):beforeCreate()、created()、beforeMount()、mounted()、beforeUpdate()、updated()、beforeDestroy()、destroyd()
在引用组件时,后面可以用v-if或者v-show来加一个判断,来决定这个页面到底要不要显示
v-if:判断为真就显示页面,否则就销毁组件,下次再用这个组件就得重新创建挂载
v-show:判断为真就显示页面,否则就隐藏页面display:none,没有销毁组件,下次再想显示这个页面直接挂载就行
vue3的生命周期:
四个阶段(创建、挂载、更新、卸载)
生命周期函数:
setup--创建--就是说不用写创建前和创建后的生命周期函数,两个状态合成一个setup
onBeforeMounte--挂载前
onMounted--挂载完毕
onBeforeUpdate--更新前
onUpdated--更新完毕
onBeforeUnmount--卸载前
onUnmounted--卸载完毕
注意:vue3中父组件和子组件谁先挂载完毕呢?
因为入口是index.html文件,它用到了main.ts文件
在main.ts文件中引入了App组件,所以App组件创建
在父App组件中首先引入的是子组件Person.vue,所以又去处理Person组件,当Person组件挂载完毕了,才会继续渲染App.vue下面的页面
父组件App.vue和子组件生命周期之间的关系
App创建-->APP挂载前-->子组件创建、挂载前、挂载完毕-->App挂载完毕
也就是说父组件App是最晚挂载完毕的
自定义Hooks
hooks:将不同事件的数据、方法、生命周期分开来,实现vue3的组合式API
1.命名规则:use+功能
2.将组件中的js代码(script内)粘贴到useDogs.ts中
3.将与本事件不相关的方法、数据全部删除
4.将本事件的数据、方法全部封装到一个函数中,并把这个函数交出去,export function和export default function的区别
5.函数内部还有return 数据和方法,通过什么类型(数组、对象·)交出去,那么在组件中引入时也要用对应类型接收
在组件中根据需要引入对应事件的hooks
import hooks文件名 from 'src'
然后在js中直接调用函数获取方法和数据即可
这样子组件的js部分就会比较整洁、可读性高
了解路由
路由其实就是一组对应关系,一个路径path对应一个组件component
当路径发生变化,路由器就会根据不同路径将不同组件放到页面上
24.5.23
路由_基本切换效果
路由其实就是一组对应关系,将一个路径与一个组件对应起来,当路径发生变化,路由器就可以通过路由规则,找到当前路径对应的组件,并将该组件呈现到页面上
使用路由步骤:
1.终端输入 npm i vue-router
2.在App.vue中编写导航栏、展示区,并且在components文件夹下面编写可能需要的组件
3.创建路由器,在src文件夹中新建router文件夹,新建index.ts文件
4.在main.ts中引入路由器,原本是创建了应用之后直接挂载(createApp(App).mount('#app')),现在是创建应用之后使用路由器再挂载
这时候就配置好了路由环境,但是当手动改变路径,比如后面加上‘/home’,页面不会出现变化,虽然路由器监测到了地址发生了变化,也找到了一组路由规则,知道了此时的地址对应的组件是Home.vue,但是当他想要把这个对应的组件呈现到页面上时会出现问题,因为它不知道要呈现到哪个位置
5.告诉路由器要把组件放到什么位置
这时候就可以通过手动在路径后面加‘/home’、‘/news’等让展示区呈现不同组件了,但是我们需要点击按钮实现展示区组件的变化,手动改变地址太不方便了
6.通过RouterLink实现点击切换展示效果
注意点
路由组件:是通过路由规则渲染到页面的组件,没有在App.vue中手动引用:<Demo />
一般组件:通过在App.vue中手动引用:<Demo />而呈现到页面中的组件
路由器的工作模式
使用history
使用hash,就是导入createWebHash
to的两种写法
1、字符串写法
2、对象写法,对象写法中还可以通过路由的名字(首先确保路由有这个属性)来跳转
建议使用对象写法
嵌套路由
嵌套路由就是一个展示区里面还有一个展示区
1.首先写希望呈现到第二个展示区的组件Details.vue,就当作一个正常页面写,只要思考你需要怎么样的效果就行
2.是希望新闻页面还有一个展示区,所以更新新闻页面的代码(右侧加一个展示区、占位等)
3.编写嵌套路由,当路径变成‘/news/details’时新闻页面的展示区呈现组件,所以Details嵌套在News里
4.通过点击新闻标题使得News里的展示区展示内容
这时候实现的效果是不论点击哪个新闻标题呈现出来的东西都是一样的,因为呈现的组件(Details.vue)是写死的,只有一种效果
路由_query参数
在传递路由的同时可以传递query参数和params参数
就是在展示新的组件的同时将本组件的一些参数传过去给新组件填充页面
首先在发生路由变化的组件,如News.vue中改变路径的同时传递参数
然后在路径变化之后引入的新组件Details.vue中接收参数
24.5.25
传递params参数
传递params参数可以直接在路径后面加上参数:
上述就是在路径变化的时候传过去三个值分别为哈哈、嘿嘿、呵呵的参数
但是这样的话会被认为三个参数是路径的一部分,计算机没有办法区分哪些是路径哪些是参数,所以首先要在这条路由规则下面占位
当路由器遇到"/news/details/哈哈/嘿嘿/呵呵"这样的路径的时候就去找对应的路由规则,就会知道后面三个是参数,且名字是id、title、contents,在被引用组件中要取得这三个参数就要有对应的名字
其次将参数传递出去,有两种写法
注意当使用对象类型的to写法传递params参数时要用路由的name属性,不能用path属性
最后在被引用的组件中获取参数
注意:传递params参数:如果用to的对象写法,只能使用name属性,不能用path属性
路由规则的props属性
传递params参数中,组件在获取参数的时候代码比较多,不方便
可以通过让路由规则的props属性为真来简化代码:
1.首先在对应的路由规则下让props属性为真
代码1:相当于<Details />,就是让Details.vue组件呈现出来
代码2:相当于<Details id='',title='',contents='' />,把收到的所有params参数作为props参数传递过去,只要子组件通过defineprops就可以得到参数
刚刚的写法只能用于传递params参数
如果写query参数的话,路由规则要这样修改
当然别的什么地方还是要按照query的要求写,Details.vue中用defineProps获取参数的写法还是一样的
props函数的参数是一个路由信息,包含很多东西,params、query什么的都有
replace属性
我们常见的网站历史记录是push模式(有历史记录),就是可以通过前进后退来访问历史页面,将历史页面都放入栈中了
但是replace属性(不留下历史记录)就是不能通过前进后退来访问历史,浏览一个新的页面就是把旧的页面替换了
在<RouteLink replace to="">这样可以使得浏览到这个页面之后就不能通过点击返回键返回了
编程式路由导航
之前的导航区都是使用<RouterLink>实现路由跳转的
但是很多时候我们希望不需要用户点击就能实现页面跳转,比如首页待三秒自动跳转到新闻页等,有时候需要点击按钮执行函数然后实现跳转,这时候函数是在脚本中,不在结构中,是不能用<RouterLink>标签的
编程式路由导航可以解决这个问题,编程式路由导航就是脱离<RouterLink>实现路由跳转
一个例子
第二个例子:通过点击新闻标题前面的按钮实现查看详情
编程式导航router.push()或者是router.replace()里面能写的东西其实和<RouterLink>中to里面能写的东西是一样的,要么字符串要么对象,可以直接把to的东西粘贴过来
路由_重定向
打开网页之后的路径其实是'/',但是我们没有编写这个组件啊,可以新增一条路由规则,重定向将某个组件当作’/‘
这样打开网址展示页的内容就不是空白了而是home组件的内容
24.5.26
一个小效果
前面一个可以根据用户在下拉框中选择的数字作为步长进行加减操作
后面一个是通过axios获取土味情话,新得到的话会呈现在第一行
搭建pinia环境
pinia:集中式状态管理工具,用于各组件之间共享数据(多个组件会用到的数据才考虑放到pinia中)
在vue2中使用的是vuex
1.终端输入:npm i pinia
2.
在vue组件中出现pinia
用pinia存储+读取数据
要想好什么组件的什么数据要放入pinia,就是某个组件你希望哪些数据可以和别的组件共享
例:把Count.vue组件的sum、LoveTalk.vue组件的talkList放入pinia
1.在src文件夹下面创建store文件夹(store就是pinia的一个具体体现)
2.在store文件夹下面创建count.ts和loveTalk.ts文件
3.将想要存储到pinia中的数据写进去
3.在组件中导入对应的store并且删除已经放入store中的数据定义,使用store中的数据
4.在网页的pinia组件中就可以看到你创建的store
pinia修改数据的三种方式
修改方法3:当你在actions中添加判断函数之后,所有组件都可以使用这个判断函数,是一种优化,复用的思想
storeToRefs
之前学过toRefs,知道在解构一个响应式对象的一些属性时会让这些属性失去响应式,所以解构的时候要用toRefs包一下
直接解构出来数据就可以在引用数据时清爽一点
原本引用时要 countStore.sum....
解构出来:直接用数据名
但是不能用toRefs来将解构出来的数据变成ref定义的响应式数据,因为这样的话他会把store中的所有数据全部变成ref的,即使是没有用过的数据
要用storeToRefs,这样他只会把数据变成ref的,不会关注方法
getters的使用
如果state中的数据需要经过处理再使用,就可以利用getters函数
$subcribe的使用
只要是store都有$subscribe函数,是订阅的意思,可以监测到store中数据的变化
使用$subscribe函数可以实现刷新不丢失,将数据保存到浏览器的本地存储中,每次进入页面都使用localStorage的数据填充页面
store的组合式写法
组件通信
一、props通信
父组件给子组件传递数据很方便,直接在引用子组件后边加上数据,子组件通过defineProps获取就行
但是子组件给父组件传递数据就比较繁琐,首先需要父组件写一个函数,写一下获取到子组件传来的数据之后做什么,然后将这个函数传递给子组件,然后子组件调用这个函数才能实现子组件传递数据给父组件,不过父组件不用再获取了,那个函数的参数就相当于子组件传递的数据,在函数中就已经编写了获取数据之后做什么,子组件调用函数之后会执行函数里的操作
这样的效果:左边是导航栏,右边是Father组件
5.29
二、自定义事件
自定义事件传递参数典型用于子组件传递数据给父组件
父组件在引用子组件时为子组件绑定自定义事件,并且确定自定义事件被触发之后执行的回调函数,然后子组件要声明这个自定义事件,并且在合适的时机触发自定义事件并传递参数,这样就会执行父组件中回调函数中的操作
三、mitt实现组件通信
使用mitt可以实现任意两个组件通信
1.首先要安装mitt,在终端输入:npm i mitt
2.然后要在src文件夹下面建一个文件夹:utils或者tools,意思是存放工具的意思
3.在utils文件夹下面建立emitter.ts文件,在该文件夹中引入mitt,用mitt创建emitter,然后暴露出去
其中的注释是为了了解emitter的用法:on是绑定,off是解绑,emit是触发,all是获得所有绑定的事件
4.在main.ts中引入emitter
5.最后,只要想要通信的两个组件都得到emitter(import emitter from ‘。。。’),
提供数据的组件触发事件(事件名,参数),
接收数据的组件绑定事件(事件名,回调函数(拿到数据之后我要怎么做)),
就可以实现组件通信
提供数据的组件:
接收数据的组件: