相关代码如下:
## 创建vue工程
npm create vue@lastest
## 安装node_modules
npm install // npm i
创建工程完毕之后,进入文件夹可以发现有如下文件,下图是文件介绍:
入口文件介绍:
这里main.ts是与index.html建立联系的;
通过.mount('#app')这样 main.ts就与index.html建立起了联系;
其中main.ts的代码格式如下:
<template>
<div>
<h1>你好</h1>
</div>
</template>
<script setup lang="ts">
// js或者ts
</script>
<style scoped>
/* 样式 */
</style>
App.vue的代码格式如下:
你好
网页调试vue可以在chrome应用商店search vue.js devtools下载安装就好;
感觉vue2和vue3的主要不同就在于vue文件中script部分;其中vue2是选项式OptionsAPI的,vue3是组合式Composition的;
选项式API相关代码如下:
注意,这里并没有setup;
组合式相关代码如下:
注意,这里的setup比beforecreate执行得还要早,setup的返回值也可以是渲染函数,data,method,setup可以同时存在,setup不可以读取data中的数据,反之则可以,因为setup是执行得最早的;
setup语法糖简化后代码如下:
响应式数据创建有两种方法,一种是ref,一种是reactive
ref定义基本类型的数据
使用的相关代码如下:
姓名:{{ name }}
// 利用大括号包起来的是不需要.value的
年龄:{{ age }}
// 利用大括号包起来的是不需要.value的
用reactive包起来后,对象变成了一个Proxy的对象,原对象在Proxy里面的target中;这里的reactive是深层次的,只能定义对象类型的响应式数据;
ref和reactive的对比
这里要注意的是,重新分配reactive的对象时,使用Object.assign,但是如果是利用ref定义的对象类数据,我们是可以直接进行替换的;
对响应式数据进行解构,toRef和toRefs:
let person = reactive({
name: '张三',
age: 18
})
let {name, age} = person
// let name = person.name
// let age = person.age
let {name, age} = toRefs(person) //把reactive对象里面的每一组对象转化为ref
// let name = toRef(person, 'name')
// let age = toRef(person, 'age')
v-bind 是 单向绑定,数据流向页面;v-model 是 双向绑定,页面也可以流向数据;
计算属性 computed 是只要响应式变量出现了变化,就随之变化;
使用例子如下:
姓名:
年龄:
information: {{ information }}
computed是用来区别于方法的,使用computed得到的属性是没有缓存的,而且computed得到的属性是不可修改的;如果需要修改,则需要使用以下方法:
姓名:
年龄:
information: {{ information }}
情况1:监视ref定义的基本类型数据
停止监视只需要调用watch函数的返回值就可以;
情况1:监视【ref】定义的值是【基本类型】的数据
sum : {{ sum }}
情况2:监视ref定义的对象类型数据
这里要注意的是:若修改的是ref定义的对象中的属性,newVal和oldVal都是新值,因为是同一个对象;若修改的是ref定义的整个对象,newVal是新值,oldVal是旧值,因为不是同一个对象;
情况2:监视【ref】定义的值是【对象类型】的数据
姓名: {{ person.name }}
年龄: {{ person.age }}
情况3:监视reactive定义的对象类型数据
很简单,只需要把ref定义的对象改为reactive定义的对象,然后在修改整个对象的时候使用Object.assign替换就可以;
情况3:监视【reactive】定义的值是【对象类型】的数据
姓名: {{ person.name }}
年龄: {{ person.age }}
情况4:监视ref或者reactive定义的对象类型中的某个属性
情况4:监视【ref或reactive】定义的【对象类型】某个属性
姓名: {{ person.name }}
年龄: {{ person.age }}
车辆:{{ person.car.c1 }}, {{ person.car.c2 }}
情况5:监视多个数据
情况4:监视【ref】定义的值是【对象类型】的数据
姓名: {{ person.name }}
年龄: {{ person.age }}
车辆:{{ person.car.c1 }}, {{ person.car.c2 }}
相较于watch,watchEffect不需要指定监视对象,而是响应式的追踪对象;
情况4:监视【ref】定义的值是【对象类型】的数据
姓名: {{ person.name }}
年龄: {{ person.age }}
车辆:{{ person.car.c1 }}, {{ person.car.c2 }}
ref属性利用ref于document.getElementById('')的区别在于,前者是局部的,不会受到整体的干扰;
北京
师范
大学
一般在文件目录src下面有一个types文件夹,types中有一个index.ts文件可以直接导入(导入过程不需要写文件)
这里文件表示泛型和接口的意思,一般是结合后端使用,避免缺失参数;
// 定义一个接口,用于限制person对象的具体属性 string,number小写
// 使用export暴露
export interface PersonInter {
id: string,
name: string,
// ?问号表示可有可无
age?: number
}
// 一个自定义类型
export type Persons = Array<PersonInter>
// 也可以这样写 export type Persons = PersonInter[]
props的使用将父组件中的数据传给子组件
vue中html标签属性前面加冒号表示表达式
<h2 a="1+1" :b="1+1">h2>
父组件如下:
子组件如下:
- {{ a.name }} - {{ a.age }}
组件的生命周期有四个阶段:创建,挂载,更新,销毁;
钩子:生命周期函数 (阶段前,阶段完毕) 钩子一共有8个
{{ message }}
Hooks是组合式功能的体现,其创建过程类似于8节中在文件目录src下创建一个hooks的文件夹,里面存放useXxxx.ts文件,实例如下:
useAdd.ts
import { computed } from '@vue/reactivity'
import {ref, onMounted} from 'vue'
export default function(){
let sum = ref(0)
let bigSum = computed(()=>{
return sum.value * 10
})
function add(){
sum.value += 1
}
onMounted(()=>{
sum.value += 1
})
return {sum, add, bigSum}
}
useDog.ts
import {reactive, onMounted} from 'vue'
import axios from 'axios'
export default function(){
let dogList = reactive([
'https://images.dog.ceo/breeds/pembroke/n02113023_4136.jpg'
])
// axios 的 实例用法1
/* function addDog(){
axios.get('https://dog.ceo/api/breed/pembroke/images/random').then(
response => {dogList.push(response.data.message)},
error => {console.log(error)}
)
} */
// axios 的 实例用法2
async function addDog() {
try {
let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
dogList.push(result.data.message)
} catch (error) {
console.log(error)
}
}
onMounted(()=>{
addDog()
})
return {dogList, addDog}
}
这样组件中可以直接应用hooks文件下的内容:
sum: {{ sum }}; bigSum: {{ bigSum }}
一般在文件目录src下面有一个types文件夹,types中有一个index.ts文件可以直接导入(导入过程不需要写文件)
路由和路由器不一样
路由类似于键值对,route
路由器类似于管理路由,router
路由的创建与Hooks和TS接口类似,其创建过程类似于8节中在文件目录src下创建一个router的文件夹,里面存放index.ts文件(无需导入文件名),实例如下:
index.ts 实例如下:
// 创建一个路由器,并暴露出去
// 第一步:引入createRouter
import {createRouter, createWebHistory} from 'vue-router'
// 第二步:引入可能要呈现的组件
import Home from '@/components/Home.vue'
import About from '@/components/About.vue'
import News from '@/components/News.vue'
// 第三步:创建路由器
const router = createRouter({
// history表示路由的工作模式
history: createWebHistory(),
routes:[// 一个个路由规则
{
path: '/home',
component: Home
},
{
path: '/news',
component: News
},
{
path: '/about',
component: About
}
]
})
export default router
在这里我们只是创建了路由,于此同时,我们需要把路由加载到项目中,所以接下来我们需要在main.ts文件中操作;
// 引入createApp 用于创建应用
import { createApp } from 'vue'
// 引入app根组件
import App from './App.vue'
// 引入路由器
import router from './router'
// 创建一个应用
const app = createApp(App)
// 使用路由器
app.use(router)
// 挂载整个应用到app容器中
app.mount('#app')
为了在App.vue引入组件,我们还需要做一些操作,使用RouterView, RouterLink:
Vue 路由测试
就完毕了,这里要用RouterLink替换a标签,并把href改成to,用RouterView表示component的插入位置;
路由组件一般放在pages或者views文件夹,一般组件通常放在components文件夹;
通过点击导航,视觉上效果消失了的组件,默认是被销毁掉的,需要的时候再去挂载;
路由器有两种工作模式,history和hash,前端一般用history,后端一般用hash;
%% 第一种: %%
<RouterLink to="/home" active-class="active">主页RouterLink>
%% 第二种: %%:
<RouterLink :to="{path='/home'}" active-class="active">主页RouterLink>
这里再介绍一下 active-class ,由于RouterLink在渲染的时候会变成a标签,所以样式可以类似于设置如下:
a.active {
text-align: center;
margin: 50px;
color: red;
}
可以在路由的index.ts文件中添加添加名字:
// 创建一个路由器,并暴露出去
// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'
// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'
// 第三步:创建路由器
const router = createRouter({
// 路由的工作模式:history模式 不带#号 优先
history: createWebHistory(),
// 路由的工作模式:hash模式 带#号
// history: createWebHashHistory(),
routes:[// 一个个路由规则
{
name: 'zhuye',
path: '/home',
component: Home
},
{
name: 'xinwen',
path: '/news',
component: News
},
{
name: 'guanyu',
path: '/about',
component: About
}
]
})
export default router
子级不用加斜杠,但是to的时候需要完整的使用\news\detail;
// 创建一个路由器,并暴露出去
// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'
// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'
import Detail from '@/views/Detail.vue'
// 第三步:创建路由器
const router = createRouter({
// 路由的工作模式:history模式 不带#号 优先
history: createWebHistory(),
// 路由的工作模式:hash模式 带#号
// history: createWebHashHistory(),
routes:[// 一个个路由规则
{
name: 'zhuye',
path: '/home',
component: Home
},
{
name: 'xinwen',
path: '/news',
component: News,
children:[
{
path: 'detail', //子级不用写斜杠
component: Detail,
}
]
},
{
name: 'guanyu',
path: '/about',
component: About
}
]
})
export default router
在传参数给组件之前,我们需要给接收参数的组件添加一个Hooks:useRoute
- 编号:{{ route.query.id }}
- 标题:{{ route.query.title }}
- 内容:{{ route.query.content }}
这里在RouterLink中添加query参数:
<ul>
<li v-for="news in NewsList" :key="news.id">
{ news.title }} -->
<RouterLink :to="{
path: '/news/detail',
query: {
id: news.id,
title: news.title,
content: news.content
}
}">
{{ news.title }}
RouterLink>
li>
ul>
首先需要在router中设置占位:就是在path中添加/:
// 创建一个路由器,并暴露出去
// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'
// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'
import Detail from '@/views/Detail.vue'
// 第三步:创建路由器
const router = createRouter({
// 路由的工作模式:history模式 不带#号 优先
history: createWebHistory(),
// 路由的工作模式:hash模式 带#号
// history: createWebHashHistory(),
routes:[// 一个个路由规则
{
name: 'zhuye',
path: '/home',
component: Home
},
{
name: 'xinwen',
path: '/news',
component: News,
children:[
{
name: 'xiangqing',
path: 'detail/:id/:title/:content?', // 需要占位 ?代表可传可不传
component: Detail,
}
]
},
{
name: 'guanyu',
path: '/about',
component: About
}
]
})
export default router
params只能使用route的name,而不能使用path,同时传参数只能使用基本类型,而不能使用数组和对象;
<ul>
<li v-for="news in NewsList" :key="news.id">
{ news.title }} -->
<RouterLink :to="{
name: 'xiangqing',
params: {// 只能传基本类型,不能是对象和数组
id: news.id,
title: news.title,
content:news.content,
}
}">
{{ news.title }}
RouterLink>
li>
ul>
props:true 布尔值写法,只能搭配params使用,path需要占位
props(route){return route.query} 函数式写法,其中route可以操作params和query
props:{id:xx,title:xx,content:xx}
其原理就相当于在 中 添加了
类似于浏览器的前进后退机制;
直接在RouterLink后面添加replace,不会留下记录;
可以做到不点标签直接跳转的效果,脱离RouterLink实现跳转;router.push() router.replace() 括号中to能怎么写,括号就能怎么写;
setTimeout(()=>{
// 在此处执行跳转
}, 3000) // 3秒后执行函数
实例代码如下:
import {onMounted} from 'vue'
import { useRouter } from 'vue-router';
const router = useRouter()
onMounted(()=>{
setTimeout(()=>{
router.push('/news')
}, 3000)
})
按钮点击实例代码如下:
-
{{ news.title }}
自动切换url,在router文件中补充规则:
{
path: '/',
redirect: '/home'
}
pinia环境首先需要安装
npm install pinia
安装之后,pinia的创建与Hooks和TS接口类似,其创建过程类似于8节中在文件目录src下创建一个store的文件夹,里面存放对应components中的文件实例如下:
在main.ts文件中需要引入pinia
import {createApp} from 'vue'
import App from './App.vue'
import {createPinia} from 'pinia'
import router from './router'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(router)
app.mount('#app')
Count.vue如下:
当前求和为: {{ sum }}, bigsum:{{ bigSum }}, smallsum:{{ smallSum }}
Count.ts 如下:
import {defineStore} from 'pinia'
import {ref} from 'vue'
export const useCountStore = defineStore('Count', {
// 写函数
actions:{
increment(){
this.sum += this.n
}
},
// 真正存储数据的地方
state(){
return {
sum: ref(0),
n: ref(1),
}
},
// 类似于vue中的computed属性
getters:{
bigSum: state=>state.sum*10,
smallSum():number{
return this.sum - 1000
}
}
})
LoveTalk.vue如下:
- {{ talk.content }}
LoveTalk.ts如下:
import {defineStore} from 'pinia'
import {nanoid} from 'nanoid' // 生成id
import axios from 'axios'
export const useLoveStore = defineStore('LoveTalk', {
actions:{
async getTalk(){
let {data:{content}} = await axios.get('https://api.uomg.com/api/rand.qinghua')
let obj = {id: nanoid(), content: content}
this.talkList.unshift(obj)
}
},
// 真正存储数据的地方
state(){
return {
talkList: JSON.parse(localStorage.getItem('talkList') as string ) || []
}
}
})
这里相对于选项式,我们需要把state中的部分转化为响应式才有效,然后添加return返回:
LoveTalk.ts如下:
import {defineStore} from 'pinia'
import {nanoid} from 'nanoid'
import axios from 'axios'
import {reactive} from 'vue'
export const useLoveStore = defineStore('LoveTalk', ()=>{
// 这里写state中的部分
let talkList = reactive(JSON.parse(localStorage.getItem('talkList') as string ) || [])
async function getTalk(){
console.log('获取一句')
try {
let {data:{content}} = await axios.get('https://api.uomg.com/api/rand.qinghua')
let obj = {id: nanoid(), content: content}
talkList.unshift(obj)
} catch (error) {
console.log('获取失败!')
}
}
return {talkList, getTalk}
})
Father.vue 如下:
这是父组件
汽车:{{car}}
获取到儿子的玩具:{{ toy }}
Child.vue如下:
这是子组件
玩具:{{ toy }}
父亲给的车:{{ car }}
标签中使用@开头的props表示的是事件,比如@click是点击事件,如果我们需要打印事件,我们可以使用$event当做参数放入函数中获取事件;
事件一般用串串的形式,而回调函数使用小驼峰
Father.vue 代码如下:
这是父组件
儿子给的玩具{{ toy }}
Child.vue 代码如下:
这是子组件
儿子的玩具{{ toy }}
类似于总线bus,收数据的:在公共区域提前绑定好事件;提供数据的:在合适的时候触发事件;
首先需要安装mitt
npm i mitt
由于mitt属于一个工具,所以在src目录下创建一个utils文件夹,创建一个emitter.ts文件;
emitter.ts 文件内容如下:
// 引入 mitt
import mitt from 'mitt'
// 调用mitt得到emitter,emitter可以绑定事件和触发事件
const emitter = mitt()
// 绑定事件
emitter.on('test1', ()=>{
console.log('test1被调用了')
})
emitter.on('test2', ()=>{
console.log('test2被调用了')
})
// 触发事件
setInterval(()=>{
emitter.emit('test1')
emitter.emit('test2')
}, 2000)
// 解绑事件
setTimeout(()=>{
emitter.off('test1')
// 全部解绑
// emitter.all.clear()
}, 3000)
// 暴露emitter
export default emitter
同时,需要在mian.ts文件中进行引入:
import {createApp} from 'vue'
import App from './App.vue'
import {createPinia} from 'pinia'
import router from './router'
import emitter from './utils/emitter'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(router)
app.mount('#app')
注意,这里不需要app.use;
Child1.vue代码如下:
这是子1
玩具 {{toy}}
Child2.vue代码如下:
这是子2
电脑 {{computer}}
玩具 {{toy}}
UI组件库底层大量使用v-model进行通信;
e
v
e
n
t
:对于原生事件,
event: 对于原生事件,
event:对于原生事件,event就是事件对象; 能==> .target
对于自定义事件,$event就是触发事件时,所传递的数据; 不能==> .target
Father.vue如下:
父组件
value : {{ username }}
自定义组件MyCustomInput.vue如下:
$event.target).value)"
>
$attrs
使用props,未用defineProps的数据,子组件会使用attrs接收;
Father.vue
父组件
{{ data }}
Child.vue
子组件
Grandson.vue
孙组件
{{ $attrs }}
$refs与$parent$refs可以获取html下所有的ref组件,$parent可以获取父组件中的数据;都要结合defineExpose()
Father.vue 代码如下:
父组件
房产:{{house}}
Child1.vue 代码如下:
子组件1
玩具:{{ toys }}
书籍:{{ books }}
Child2.vue 代码如下:
子组件1
电脑:{{ computer }}
书籍:{{ books }}
常用于祖孙之间越过父亲直接通信;
祖使用provide传送,孙使用inject接收,inject第二个参数是默认值
Father.vue
父组件
钱:{{ money }}
车:{{ car.brand }} 价值 :{{ car.price }}
Child.vue
孙组件
钱:{{ money }}
车:{{ car.brand }} 价值 :{{ car.price }}
slot有三种:默认插槽,具名插槽,作用域插槽;
默认插槽就是在组件中间使用html标签,然后在组件中加入;
Father.vue 如下:
热门游戏列表
- {{ game.name }}
热门美食列表
热门美食列表
Child.vue 如下:
具名插槽意思就是具有名字的插槽,这里默认插槽的名字为default,多个slot可以倒序;
Category.vue
Father.vue
热门游戏列表
- {{ game.name }}
热门美食列表
热门美食列表
主要用在子里面有数据,但是父组件想以不同形式进行表示;
Game.vue
游戏列表
Father.vue
- {{ y.name }}
- {{ y.name }}
{{ y.name }}
customRef() 预期接收一个工厂函数作为参数,这个工厂函数接受 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set 方法的对象。
一般来说,track() 应该在 get() 方法中调用,而 trigger() 应该在 set() 中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。
示例 创建一个防抖 ref,即只在最近一次 set 调用后的一段固定间隔后再调用:
import { customRef } from 'vue'
export function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
// 数据被读取时,调用get
get() {
track() // 告诉vue数据value很重要,一旦value变化就去更新
return value
},
// 数据被修改时,调用set
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger() // 通知一下vue数据value变化了
}, delay)
}
}
})
}
这个组件中有一个 当在初始 HTML 结构中使用这个组件时,会有一些潜在的问题: 这个模态框的 Hello from the modal! 在这个组件树中有多个嵌套组件,要渲染出它们,首先得解析一些异步资源。如果没有 有了 按钮来触发打开模态框,和一个 class 名为 .modal 的 position: fixed 能够相对于浏览器窗口放置有一个条件,那就是不能有任何祖先元素设置了 transform、perspective 或者 filter 样式属性。也就是说如果我们想要用 CSS transform 为祖先节点 z-index 受限于它的容器元素。如果有其他元素与 z-index,则它会覆盖住我们的模态框。
提供了一个更简单的方式来解决此类问题,让我们不需要再顾虑 DOM 结构的问题。让我们用 改写一下 :
40. Suspense
,则它们每个都需要处理自己的加载、报错和完成状态。在最坏的情况下,我们可能会在页面上看到三个旋转的加载态,在不同的时间显示出内容。 组件后,我们就可以在等待整个多层级组件树中的各个异步依赖获取结果时,在顶层展示出加载中或加载失败的状态。 可以等待的异步依赖有两种:setup() 钩子的组件。这也包含了使用 时有顶层 await 表达式的组件。<Suspense>
<Dashboard />
<template #fallback>
Loading...
template>
Suspense>
41. 全局api转化为应用对象
import {createApp} from 'vue'
import App from './App.vue'
import Hello from './Hello.vue'
//创建应用
const app = createApp(App)
// 定义全局组件
app.component('Hello',Hello)
// 定义全局属性
app.config.globalProperties.x = 99
declare module 'vue'{
interface ComponentCustomProperties{
x: number
}
}
// 定义样式 对组件使用v-beauty改变样式
app.directive('beauty',(element,{value})=>{
element.innerText += value
element.style.color = 'green'
element.style.backgroundColor = 'yellow'
})
// app挂载
app.mount('#app')
// app卸载
setTimeout(()=>{
app.unmount()
}, 3000)
42. 其他
智能网联汽车网络安全攻击与防御技术概述
人力资源管理系统如何促进业务增长
linux7上powerpath卸载
3D RPG Course | Core 学习日记三:Navigation智能导航地图烘焙
post请求同时上传文件并传递其他参数的前后端写法
算法学习day02(链表/哈希表/字符串)
键盘按键Home & End的妙用
中国水稻行业供需现状发展趋势分析 国稻种芯百团计划行动
设计模式——责任链模式