生活札记
Vue3学习笔记 - 高阶(三)
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
教程:https://www.bilibili.com/video/BV1su411D7GV
参考:https://blog.csdn.net/weixin_43931876/article/details/120058286
1、TypeScript(JavaScript超集):
2)、https://www.typescriptlang.org/zh/
2、安装TypeScript:
1)、下载安装nodejs
2)、npm镜像改为国内:
npm get registry
npm config set registry=https://registry.npm.taobao.org/
或者安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org/
3)、安装typescript:npm install -g typescript@latest
4)、查看版本:tsc -v
5)、安装ts-node(运行ts):npm install -g ts-node
3、TypeScript变量申明、变量类型:
// 变量声明
let num:number = 100
num = 200
console.log(num)
// 变量自动推断
let num2 = 300
console.log(num2)
// 变量声明
let stringA:string = "飓风呀"
stringA = "哈哈哈"
console.log(stringA)
// 函数变量声明
function count(a:number, b:number) {
return a+b
}
console.log(count(1, 2))
4、TypeScript类型:
// 类型
// 1、boolean(布尔)、string(字符串)、number(数字)、array(数组)
// 2、null(null不存在)、undefined(定义了未初始化)、any(任意)、unknown(另一个any)
// 3、tuple(元组,类似数组)、void(没有任何类型)、never(永不存在)、enum(枚举)
// boolean
let isshow:boolean = true
if (isshow) {
console.log("true")
}
// string
let a:string = "飓风呀"
// number
let b:number = 1
// Array
let arr:number[]
arr = [1,2,3]
// 字面类型、组合类型
let c:100|200
c = 200
let d:number|string
d = 100
d = "飓风呀"
// any、unknown
let e:any
e = 100
e = "飓风呀"
let f:unknown
f = 10
f = "飓风呀"
// array、object
let g:number[]
g = [1,2,3]
let h:Array<string>
h = ["1","2"]
// let i:object
let i = {
username:"飓风呀",
age:28
}
// 对象解构
let { username, age} = i
console.log(username, age)
// 展开运算符号...
let j = {
...i,
sex:"男"
}
console.log(j)
// void、never:void代表返回空,never不会有返回
// void限定返回返回null、undefined
// never 类似无限循环或者报错
function testvoid(a:number):void {
console.log(a)
return undefined
}
testvoid(1)
function testnever(a:number):never {
// 函数不结束,死循环
while (true) {
}
// 抛出异常
throw new Error("错误")
}
// tuple 元组
let k:[number,string,boolean,number]
k = [1,"2",true,3]
// ?代表可选,前面是必选,可选后面不能跟着必选
let l: [number, string, boolean?]
l = [1, "2", true]
l.push("aaa") // tuple操作与数组类型
console.log(l)
// enum 各种情况的语义化
enum m {
ma = 0,
mb = 1,
mc = 2,
md = "飓风呀"
}
// 常数枚举,会被隐藏
const enum n {
ma = 0,
mb = 1,
mc = 2,
md = "飓风呀"
}
// 类型别名(自定义类型,可以设置为联合类型)
type mytype = string|number
let o:mytype
o = "aaa"
o = 100
5、TypeScript面向对象:
// 类、面向对象(与PHP的面向对象有点类似)
abstract class Person {
// 属性
public name:string // 公有属性
protected age:number // 保护属性
private sex:string // 私有属性
static job:string // 静态属性
// 构造函数
constructor(name:string, age:number, sex:string, job:string) {
this.name = name
this.age = age
this.sex = sex
Person.job = job // 静态无法用this获取
}
// 方法
move():void{
console.log("姓名:" + this.name + ",年龄:" + this.age + ",性别:" + this.sex + ",工作:" + Person.job)
}
}
// extends 继承
class User extends Person {
// 类内属性
public girlfriend:string
// 类内构造函数
constructor(name: string, age: number, sex: string, job: string, girlfriend:string) {
// 执行父类构造函数
super(name, age, sex, job)
// 类内属性
this.girlfriend = girlfriend
}
// 重写方法
move(){
console.log("女朋友是:" + this.girlfriend)
// 执行父类方法
super.move()
}
// 自定义方法
biaobai(){
console.log("跟:" + this.girlfriend +",表白了啊")
}
}
// 实例化 Person,如果类定义了 abstract(抽象类) 则不能直接实例化,只能被继承
// let person = new Person("飓风呀", 100, "女", "GO")
// person.move()
// 实例化 User
let user = new User("飓风呀",100,"女","GO","小王子")
user.biaobai()
user.move()
// interface 接口
interface Person2 {
name:string
move():void
}
// interface 接口继承必须严格重写接口里所有属性方法
class User2 implements Person2 {
// 属性
public name:string
// 构造函数
constructor(name:string){
this.name = name
}
// 重写方法
move():void{
console.log(this.name)
}
}
// 实例化 User2
let user2 = new User2("飓风呀");
user2.move()
6、TypeScript泛型:
// 泛型
function funa<T,K>(name:T,age:K):T {
console.log(name)
console.log(age)
return name
}
funa("飓风呀",100)
funa(100,"飓风呀")
// 类
class Person3<T,K> {
// 属性
name:T
age:K
// 构造函数
constructor(name:T,age:K){
this.name = name
this.age = age
}
// 方法
move():T{
console.log(this.name + "" + this.age)
return this.name
}
}
// 实例化类
let person = new Person3("飓风呀", 100)
person.move()
let person2 = new Person3(100,"飓风呀")
person2.move()
7、tsconfig.json:ts编译配置
1)、初始化配置问:tsc --init
2)、编译:tsc -w
3)、配置文件:tsconfig.json
{
// 包含
"include":[
"./src/*" // 编译src目录下所有文件
],
// 排除
"exclude": [
"./exclude/*" // 排除exclude目录下所有
],
// 文件
"files": [
"./02-type.ts"
],
// 编译选项配置
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
8、Vite:https://vitejs.cn/
1)、vite创建项目:npm create vite
2)、安装volar插件:Vue Language Features (Volar)、Volar代替vetur
3)、vite.config.ts配置文件:安装库包(node环境支持js):npm i @types/node --save-dev
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
// 插件
plugins: [vue()],
// 服务,启动之后变成 http://127.0.0.1:3001
server:{
port:3001
},
// 决定
resolve:{
// 别名
alias:{
"@":path.resolve(__dirname, "src"),
"com":path.resolve(__dirname, "src/components")
}
}
})
模板导入路径简化:
// import HelloWorld from '@/components/HelloWorld.vue'
import HelloWorld from 'com/HelloWorld.vue
9、Setup语法糖:
1)、App.vue:
<script setup lange="ts">
// 每个组件都是独立的实例
import { ref, reactive, onMounted } from "vue"
import SetupYes from "./views/SetupYes.vue"
// 常量
const title = ref("app")
// 对象
const obj = reactive({
username:"飓风呀",
age:[1,2,3,4]
})
// 方法
const parentClick = (val) => {
alert(val)
}
// 获取ref子组件,名称必须与ref的值一致
let setupRef = ref(null)
// 子组件ref(TypeScript语法)
// const setupRef = ref<InstanceType<typeof SetupYes >>()
// 挂载
onMounted(() => {
console.log(setupRef.value.mydata)
})
</script>
<template>
<div>
<h1>{{ title }} - {{ obj.username }}</h1>
</div>
<SetupYes msg="Hei Setup" :title="title" data-id="888" :ageArr="obj.age" @parentClick="parentClick" ref="setupRef">
<template #header>
<h2>我是slot插槽哈哈哈</h2>
</template>
</SetupYes>
</template>
2)、SetupYes.vue子组件:
<template>
<div>
<p>SetupYes</p>
<p>属性msg:{{msg}}</p>
<p>属性ageArr:{{ageArr}}</p>
<p><button type="button" @click="clickEmits">点击Emit事件</button></p>
<slot name="header">slottest</slot>
</div>
</template>
<script setup>
import { ref, useSlots, useAttrs, onMounted } from "vue";
// defineProps:属性
// defineProps(
// {
// msg:{
// type:String,
// defult:"aaaa",
// required:true
// },
// ageArr:{
// type:Array,
// defult(){
// return [10]
// }
// }
// }
// )
// 默认值
const props = withDefaults(
// 定义默认属性的类型 ? 表示可有可无
defineProps<{
msg?:string,
// ageArr:[number]
ageArr: Array<number>
}>(),{
// 默认值
msg:"aaaa",
ageArr:() => [10]
}
)
// defineEmits:事件
// const emits = defineEmits(["parentClick"])
const emits = defineEmits<
{
(e:"parentClick", data:string):void
}
>()
const clickEmits = () => {
emits("parentClick", "Hei Hello") // 这个emits与上面的定义的 emits 保持一致
}
// defineExpose:暴露属性
const mydata = ref(666)
defineExpose({ mydata })
// useSlots()、useAttrs():属性如果在defineProps定义了,那这里就不输出
const slots = useSlots()
const atrrs = useAttrs()
onMounted(() => {
console.log(slots.header && slots.header()) // slots.header() 是个方法
console.log(atrrs)
})
</script>
3)、toRefs:
// reactive声明响应式数据,用于声明引用数据类型
const state = reactive({
name: 'Jerry',
sex: '男'
})
// 使用toRefs解构,template可直接使用{{name}}、{{sex}}
const {name, sex} = toRefs(state)
4)、watch:三个参数,1-属性方法,2-新值旧值,3-配置参数
// 监听count
watch(
() => state.count,
(newVal, oldVal) => {
console.log(state.count)
console.log(`watch监听变化前的数据:${oldVal}`)
console.log(`watch监听变化后的数据:${newVal}`)
},
{
immediate: true, // 立即执行
deep: true // 深度监听
}
)
5)、nextTick():
<template>
<child ref='childRef'/>
</template>
<script setup>
import { ref, nextTick } from 'vue'
// 引入子组件
import child from './child.vue'
// 子组件ref(TypeScript语法)
const childRef = ref<InstanceType<typeof child>>()
// nextTick
nextTick(() => {
// 获取子组件name
console.log(childRef.value.name)
// 执行子组件方法
childRef.value.changeName()
})
</script>
6)、插槽slot:
子组件
<template>
// 匿名插槽
<slot/>
// 具名插槽
<slot name='title'/>
// 作用域插槽
<slot name="footer" :scope="state" />
</template>
<script setup>
import { useSlots, reactive } from 'vue'
const state = reactive({
name: '张三',
age: '25岁'
})
const slots = useSlots()
// 匿名插槽使用情况
const defaultSlot = reactive(slots.default && slots.default().length)
console.log(defaultSlot) // 1
// 具名插槽使用情况
const titleSlot = reactive(slots.title && slots.title().length)
console.log(titleSlot) // 3
</script>
父组件
<template>
<child>
// 匿名插槽
<span>我是默认插槽</span>
// 具名插槽
<template #title>
<h1>我是具名插槽</h1>
<h1>我是具名插槽</h1>
<h1>我是具名插槽</h1>
</template>
// 作用域插槽
<template #footer="{ scope }">
<footer>作用域插槽——姓名:{{ scope.name }},年龄{{ scope.age }}</footer>
</template>
</child>
</template>
<script setup>
// 引入子组件
import child from './child.vue'
</script>
7)、路由useRoute和useRouter:
<script setup>
import { useRoute, useRouter } from 'vue-router'
// 必须先声明调用
const route = useRoute()
const router = useRouter()
// 路由信息
console.log(route.query)
// 路由跳转
router.push('/newPage')
</script>
8)、路由导航守卫:
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
// 添加一个导航守卫,在当前组件将要离开时触发。
onBeforeRouteLeave((to, from, next) => {
next()
})
// 添加一个导航守卫,在当前组件更新时触发。
// 在当前路由改变,但是该组件被复用时调用。
onBeforeRouteUpdate((to, from, next) => {
next()
})
</script>
9)、Vuex:*Vue3 中的Vuex不再提供辅助函数写法
<script setup>
import { useStore } from 'vuex'
import { key } from '../store/index'
// 必须先声明调用
const store = useStore(key)
// 获取Vuex的state
store.state.xxx
// 触发mutations的方法
store.commit('fnName')
// 触发actions的方法
store.dispatch('fnName')
// 获取Getters
store.getters.xxx
</script>
10)、Pinia:Pinia 正式成为 Vue 官方的状态库,意味着 Pinia 就是 Vuex 5:https://pinia.web3doc.top/
Pinia 的优点:
1)、同时支持 Composition Api 和 Options api 的语法;
2)、去掉 mutations ,只有 state 、getters 和 actions ;
3)、不支持嵌套的模块,通过组合 store 来代替;
4)、更完善的 Typescript 支持;
5)、清晰、显式的代码拆分;
安装
# 使用 npm
npm install pinia
# 使用 yarn
yarn add pinia
main.js 引入
import App from './App.vue'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
配置 store.js
import { defineStore } from 'pinia'
// defineStore 调用后返回一个函数,调用该函数获得 Store 实体
export const useStore = defineStore({
// id: 必须,在所有 Store 中唯一
id: 'globalState',
// state: 返回对象的函数
state: () => ({
count: 1,
data: {
name: 'Jerry',
sex: '男'
}
}),
// getter 第一个参数是 state,是当前的状态,也可以使用 this 获取状态
// getter 中也可以访问其他的 getter,或者是其他的 Store
getters: {
// 通过 state 获取状态
doubleCount: (state) => state.count * 2,
// 通过 this 获取状态(注意this指向)
tripleCount() {
return this.count * 3
}
},
actions: {
updateData (newData, count) {
this.data = { ...newData }
this.count = count
// 使用 $patch 修改多个值
this.$patch({
data: { ...newData },
count
})
}
}
})
使用 store
<template>
// 获取 store 的 state
<p>姓名:{{store.data.name}}</p>
<p>性别:{{store.data.sex}}</p>
// 调用 actions方法 / 修改 store
<button @click='update'>修改用户信息</button>
// 获取 getter
<p>获取getter:{{store.doubleCount}}</p>
</template>
<script setup>
import { useStore } from '@store/store.js'
const store = useStore()
function update () {
// 通过 actions 定义的方法修改 state
store.updateData({ name: 'Tom', sex: '女' })
// 通过 store 直接修改
store.data = { name: 'Tom', sex: '女' }
// 同时改变多个状态
store.$patch((state) => {
state.data = { name: 'Tom', sex: '女' }
state.count = 2
})
}
</script>
其他方法
替换整个 state
$state 可以让你通过将 store 的属性设置为新对象来替换 store 的整个 state
const store = useStore()
store.$state = {
name: 'Bob',
sex: '男'
}
重置状态
调用 store 上的 $reset() 方法将状态重置为初始值
const store = useStore()
store.$reset()
11)、provide和inject:
父组件
<template>
<child/>
</template>
<script setup>
import { ref, watch, provide } from 'vue'
// 引入子组件
import child from './child.vue'
let name = ref('Jerry')
// 声明provide
provide('provideState', {
name,
changeName: () => {
name.value = 'Tom'
}
})
// 监听name改变
watch(name, () => {
console.log(`name变成了${name}`)
setTimeout(() => {
console.log(name.value) // Tom
}, 1000)
})
</script>
子组件
<script setup>
import { inject } from 'vue'
// 注入
const provideState = inject('provideState')
// 子组件触发name改变
provideState.changeName()
</script>
12)、Vue3:Typescript与组合式API、defineProps、defineEmits等使用: https://blog.csdn.net/qq_14818715/article/details/128265658
10、简单项目:vue3.2+setup语法糖+ts+elementPlus+vue-router+vuex
教程:https://www.bilibili.com/video/BV1Su411q71n
安装:
1)、安装vite:npm create vite,选择vuets
2)、node环境支持js:npm install @types/node --save-dev
3)、vue-router:npm install vue-router@next
4)、vuex:npm install vuex@next --save
5)、 element-plus:npm install element-plus --save,加载方式:https://element-plus.gitee.io/zh-CN/guide/quickstart.html
6)、sass(css):npm install sass-loader sass -D
7)、vscode自定义快捷模板:文件 >> 首选项 >> 配置用户代码片段
8)、element-plus全局图标:npm install @element-plus/icons-vue
文明上网理性发言!
已完结!!!