vue3 基础
Vue3 语法
第一步:
/**
* 首先要求引入 defineComponent 和 可选的 ref
* defineComponent 用于解决 Ts 下传统的 Vue.extends 无法对组件给出正确参数类型推断
* 即 在 ts 环境中如果参数类型推断不对, 使用 defineComponent 包装
*/
import { defineComponent, ref } from "vue";
export default defineComponent({
})
Setup & ref 方法
/**
* 有了 Setup 方法
* 无需再写 data methods 等对象
*
* 要在 template 中使用变量 必须先使用 ref 包装变量
* 在 script 中 通过 变量名.value 访问
* 在 template 中 可以直接通过 变量名 访问
*/
import {defineComponent, ref} from 'vue'
export default defineComponent({
name: 'app',
props: {
name: {
type: string,
default: 'abcd'
}
},
/**
* setup 方法 有两个参数
* props 和 context 两个参数
* context 是一个对象
* 有 attrs、 slots、emit、expose
* attrs 等于 $attrs
* slots 等于 $slots
* emit 等于 $emit
* expose 暴露公共 property (函数)
*/
Setup (props, context) {
const name = ref('名称')
const click = function () {
console.log(abcd)
}
const abcd = props.name // abcd
// 只有通过 return 返回的 才能在 外部访问
// abcd 没有 return 因此在 template 中无法访问
return {
name,
click
}
}
})
reactive 方法
/**
* reactive 接收一个对象作为参数
* 无论变量或方法 都可以作为 Object 的属性
*/
import {ref, reactive} from 'vue'
export default {
name: 'app',
setup () {
const obj = reactive({
name: ['1', '2', '3'],
click: index => obj.name[index]
})
return {
data
// name: data.name,
// click: data.click
}
}
}
Vue3 + TypeScript 类型注释
/**
* 先定义一个接口作为规范
*/
interface DataProp {
name: string
age: number
work: string[]
}
export default {
name: 'app',
setup () {
// 使用接口
const data: DataProp = {
name: 'a',
age: 1,
work: ['2']
}
}
}
toRefs 方法
/**
* 输出变量都用 data 会很麻烦
* 但是 使用 ... 运算符会导致变量不再有响应式功能
* toRefs 解决了这个问题
*/
import {reactive, toRefs} from 'vue'
export default {
setup () {
const data = reactive({
name: 'abcd'
})
const refData = toRefs(data)
return {
// 进行解构
// 可以在 template 中 直接通过 name 访问 data.name
...refData
}
}
}
关于 ref 和 reactive
都可以使用,作用都是生成响应式对象,目前只是编写方式不同
Vue3 的生命周期和钩子函数
Vue2 的生命周期方法也可以在 setup() 函数中编写,使用
需要通过 import 从 vue 中导入
都接收一个回调函数作为参数
| 生命周期 | 描述 |
| ——– | ———————————————————— |
| setup() | 在创建组件之前 在 beforeCreate 和 created 前执行 创建 data method |
| onBeforeMount() |组件挂载到节点上之前执行|
| onMounted() |组件挂载完成执行|
| onBeforeUpdate() |组件更新前执行|
| onUpdated() |组件更新后执行|
| onBeforeUmmount() |组件卸载前执行|
| onUmmounted() |组件卸载后执行|
| onActivated() |包含在 `<keep-alive>
` 中的组件 会多次两个生命周期钩子函数 被激活执行|
| onDeactivated() |组件切换时,旧组件消失时执行|
| onErrorCaptured() |捕获一个来自子孙组件的异常时执行|
| onRenderTracked |状态跟踪,会跟踪页面上所以响应式变量和方法的状态,也就是 return 的值都会追踪,只要页面有 update 的情况就会跟踪并生成 event 对象 可以通过 event 对象查找问题所在|
| onRenderTriggered |状态触发,不会跟踪值,而是给予变化值的信息,并将新旧值明确列出|
在 `<keep-alive>
` 中的组件会将数据保存在 内存中
Vue3 Watch
/**
* watch 需要导入才能使用
*/
import {..., watch} from 'vue'
export default {
setup () {
const data = reactive({
name: 'wa',
age: 2
})
// 单个值
// watch (data.name, (newValue, oldValue) => {})
// 多个值时 传入数组 而非多写 watch 返回的也是数组 n[index], o[index]
// 错误
// watch ([data.name, data.age], (n, o) => {})
// vue3 不能监听 reactive 中的值 需要函数返回
watch(() => ...data, (n, o) => {})
return {
data
}
}
}
独立模块化
/**
* 更新时间模块
* filename: useUpdateTime.ts
* 需要通过 import 导入 ref
* 并通过 export 导出
*/
import { ref } from 'vue'
const getNowTime = () => {
const now = new Date();
const hour = now.getHours() < 10 ? "0" + now.getHours() : now.getHours();
const minu =
now.getMinutes() < 10 ? "0" + now.getMinutes() : now.getMinutes();
const sec =
now.getSeconds() < 10 ? "0" + now.getSeconds() : now.getSeconds();
nowTime.value = hour + ":" + minu + ":" + sec;
setTimeout(getNowTime, 1000);
};
export { nowTime, getNowTime }
Teleport 瞬间移动
可以将组件挂载到任何想挂载的 DOM 上 , 而不必嵌套在 #app 中
<!-- DOM-ID 为 需要挂载的 DOM 的 id 值 -->
<template>
<teleport to="DOM-ID">
<model/>
</teleport>
</template>
Suspense 异步请求组件
Suspense 组件提供两个 template 位置 一个是请求没有返回时的内容,一个是请求完成的内容
如果使用 Suspense ,要返回一个 promise 对象 而非 JSON 对象
<template>
<Suspense>
<template #default>
加载完成
</template>
<template #fallback>
加载中...
</template>
</Suspense>
</template>
处理异常请求错误
在 vue3 中 可以使用 onErrorCaptured 捕获异常
import { ref, onErrorCaptured } from 'vue'
export default {
setup(){
onErrorCaptured(error => console.log(error))
}
}
使用 Pages 开发多页应用
单页应用和多页应用的区别在于
- 单页应用只加载一次公共组件资源,仅会刷新局部资源
- 多页应用每切换一个页面就要重新加载所有资源
| | 单页面应用(SinglePage Web Application,SPA) | 多页面应用(MultiPage Application,MPA) |
| —- | ——————————————— | ————————————— |
| 组成| 一个外壳页面和多个页面片段组成 | 多个完整页面构成 |
| 资源共用(css,js) | 共用,只需在外壳部分加载 | 不共用,每个页面都需要加载 |
| 刷新方式 | 页面局部刷新或更改 | 整页刷新 |
| url模式 | a.com/#/pageone a.com/#/pagetwo | a.com/pageone.html a.com/pagetwo.html |
| 用户体验 | 页面片段间的切换快,用户体验良好 | 页面切换加载缓慢,流畅度不够,用户体验比较差 |
| 转场动画 | 容易实现 | 无法实现 |
| 数据传递 | 容易 | 依赖 url传参、或者cookie 、localStorage等 |
| 搜索引擎优化(SEO) | 需要单独方案、实现较为困难、不利于SEO检索 可利用服务器端渲染(SSR)优化 | 实现方法简易 |
| 试用范围 | 高要求的体验度、追求界面流畅的应用 | 适用于追求高度支持搜索引擎的应用 |
| 开发成本 | 较高,常需借助专业的框架 | 较低 ,但页面重复代码多 |
| 维护成本 | 相对容易 | 相对复杂 |
单页面应用时,vue 项目中会有一个入口文件 main.js
则构建多页应用时,会有多个入口文件
在 src 文件夹下 创建 pages 文件夹存放子页面
在 pages 下新建文件夹 childPages(可自定义的文件夹名称,与页面名称一致) 表示子页面
在 childPages 文件夹下创建文件夹 components、views,根组件 childPages.vue 和 入口文件 childPages.js
然后在 public 文件夹下创建 childPages.html 表示子页面主体
静态资源、路由、store最好通用,静态资源存放在 src/assets/ 下 如果每个子页面创建一个静态资源文件夹,打包时会出问题
修改子页面入口文件夹
// childPages.js import { createApp } from 'vue' import childPages from './childPages.vue' import router from '@/router' import store from '@/store' createApp(childPages).use(store).use(router).mount('#app')
然后配置 vue.config.js
// vue.config.js // 没有则在根目录下创建 module.exports = { pages: { // 主页 index: { // 入口文件 entry: './src/main.js', // html 文件 template: './public/index.html', // 页面标题 title: '首页' }, // 其他页面 childPages: { entry: './src/pages/childPages/childPages.js', template: './public/childPages.html', title: '子页面' } } }
设置多页应用时,不能配置 publicPath 为 ./ 否则无法访问其他页面