循序渐进Vue.js 3前端开发实战
2022-12-06 12:13:45 3 举报
AI智能生成
《循序渐进Vue.js 3前端开发实战》笔记
作者其他创作
大纲/内容
八、动画
CSS3原生动画
transition简单过渡动画
方式1:使用transition属性指定过渡动画
transition: width 2s, height 2s, background-color 2s;
方式2:使用逐条属性对动画效果进行设置
transition-property: width, height, background-color;
transition-duration: 1s;
transition-timing-function: linear;
transition-delay: 2s;
keyframes关键帧动画
定义关键帧
@keyframes animation1 {
0% {样式详情}
50% {样式详情}
100% {样式详情}
}
0% {样式详情}
50% {样式详情}
100% {样式详情}
}
或则只定义起始状态
@keyframes animation1 {
from {样式详情}
to {样式详情}
}
@keyframes animation1 {
from {样式详情}
to {样式详情}
}
使用
通过animation属性使用关键帧
.clazzName {
其他样式
animation: animation1 4s linear;
}
其他样式
animation: animation1 4s linear;
}
使用逐条属性对动画效果进行设置
animation-name、duration、timing-function、direction、iteraction-count、play-state、delay、fill-mode
使用JS方式实现动画
定时器更新组件状态
setInterval(()=>{更新组件状态}, 10)
Vue过渡动画
定义过渡动画
Vue使用transition内置组件,包装展示过渡动画的组件
动画的定义
定义6组CSS属性样式,其中x为组件transition的name值
x-enter-from:即将插入页面
x-enter-active:插入过程整个过渡动画都会添加
x-enter-to:组件被插入页面后
x-leave-from:移除前的初始状态
x-leave-active:移除过程整个过渡动画都会添加
x-leave-to:移除后的结束状态
x-enter-from:即将插入页面
x-enter-active:插入过程整个过渡动画都会添加
x-enter-to:组件被插入页面后
x-leave-from:移除前的初始状态
x-leave-active:移除过程整个过渡动画都会添加
x-leave-to:移除后的结束状态
active中的动画行为可使用transition或animation方式
组件的包装
<transition name="ani">需要做动画的组件</transtion>
包装的组件在插入或移除的时,自动寻找动画名称开头的CSS类
动画过程中的监听回调
transition组件的监听回调函数
before-enter、enter、after-enter、enter-cancelled
before-leave、leave、after-leave、leave-cancelled
before-leave、leave、after-leave、leave-cancelled
使用实例
<transtion name="ani" @enter="funcEnter">包装的组件</transition>
在组件的methods方法中定义回调方法的实现
多个组件的过渡动画
默认transition内部组件的插入和移除同步进行
先执行移除再插入:transition组件的mode属性设置为out-in
<transition name="ani" mode="in-out">包装的组件</transition>
列表的过渡动画
使用transition-group内置组件包装v-for组件实现对列表元素变动的动画
<transition-group name="ani">
<div v-for="item in list">元素组件</div>
</transition-group>
<div v-for="item in list">元素组件</div>
</transition-group>
区别组件过渡动画
要求:列表中每一个元素有唯一的key值
ani-move特殊动画类:实现对排序过程动画进行定义
九、构建工具Vue CLI
Vue CLI入门
安装
安装nodejs
安装Vue CLI:npm install -g @vue/cli
升级Vue CLI:npm update -g @vue/cli
快速创建项目
使用命令创建
vue create hello-world
使用图形界面交互创建
vue ui
Vue CLI项目模板工程
模板工程结构
文件
.gitignore:git版本忽略文件
babel.config.js:Babel工具的配置文件,Babel一个JavaScript编译器
package.json:存储JSON对象,配置当前项目名称、版本号、脚本命令以及模块依赖等
yarn.lock:记录YARN报管理器安装的依赖版本信息,无需关心
babel.config.js:Babel工具的配置文件,Babel一个JavaScript编译器
package.json:存储JSON对象,配置当前项目名称、版本号、脚本命令以及模块依赖等
yarn.lock:记录YARN报管理器安装的依赖版本信息,无需关心
文件夹
node_modules:存放npm安装的依赖模块
public:共有的资源文件,图标、HTML文件等
src:核心功能代码,asserts存放资源文件,components存放组件文件
public:共有的资源文件,图标、HTML文件等
src:核心功能代码,asserts存放资源文件,components存放组件文件
vue文件结构
template模板部分、script脚本代码、style样式代码
允许Vue项目工程
命令方式
npm run dev
指定运行端口:npm run serve -- --port 9000
交互界面方式
vue ui
在项目中使用依赖
命令方式
npm install --save axios vue-axios
会自动更新package.json并保存依赖到node_modules文件夹下
交互界面方式
vue ui
依赖-安装依赖
工程构建
命令方式
npm run build
交互界面方式
vue ui
任务-build
构建结果:dist文件夹,包含压缩后的入口文件index.html、静态资源、CSS、JavaScript等文件
新一代构建工具Vite
对比Vue CLI
不基于webpack,轻量,功能仅构建和开发服务器
体验Vite
使用Vite创建Vue项目
npm init @vitejs/app
十、基于Vue3的UI组件库 - Element Plus
入门
安装
引入样式
element-plus/lib/theme-chalk/index.css
引入组件库
element-plus/lib/index.full.js
Vue挂载Element
Vue.createApp({}).use(ElementPlus)
按钮组件
el-button
属性:type、disable等
标签组件
el-tag
属性:type、closeable、click、close
el-check-tag
属性:checked
空态图与加载占位图
el-empty
属性:image等
插槽:image、description
插槽:image、description
el-skeleton
属性:animated、count、rows、throttle等
el-skeleton-item
图片与头像
el-image
属性:fit、src、load、error、lazy
el-avatar
属性:src
表单类
单选框
el-radio-group
属性:v-model、disable、change
el-radio
属性:label
el-radio-button
属性:label
复选框
el-checkbox-group
属性:min、max、v-model
el-checkbox
属性:label
标准输入框
el-input
属性:type、change等
插槽:prefix、suffix、prepend、append
插槽:prefix、suffix、prepend、append
带推荐列表的输入框
el-autocomplete
属性:disabled、placement、fetech-suggestions、select、change
插槽:prefix、suffix、prepend、append
插槽:prefix、suffix、prepend、append
数字输入框
el-input-number
属性:min、max、precision、change、blur、focus
选择列表
el-select
属性:multiple、disabled、clearable、remote-method、change、remote-tag、clear、focus
el-option-group
el-option
多级列表组件
el-cascader
属性:options、props
开关与滑块
开关组件
el-switch
属性:loading、before-change、change
滑块组件
el-slider
属性:min、max、step、change、input
选择器
时间选择器
el-time-picker
属性:is-ranage、default-value、format、change、blur、focus
日期选择器
el-date-picker
属性:type、format、validate-event、change、blur、focus
type为datetime时同时选择日期和时间
颜色选择器
el-color-picker
属性:show-alpha、color-format、predefine
提示类
警告
el-alert
属性:type、description、effect、close
消息提示
el-button
属性:message、type、duration、showClose、center、onClose
Element-Plus注册的全局方法
$message方法:直接使用this调用发起消息提示
$msgbox方法:用户进行交互
通知
全局方法$notify
参数:title、type、duration、position、showClose、onClose、onClick、offset
数据承载相关组件
表格
el-table
属性:data
el-table-column
属性:prop、label、sortable
导航菜单
el-menu
属性:mode、select、open
el-submenu
属性:index、show-timeout
el-menu-item
属性:index、route
标签页
el-tabs
属性:editable、closeable、addable、before-leave、tab-click
el-tab-pane
属性:label、name、lazy
插槽:label
插槽:label
抽屉
el-drawer
属性:direction
布局容器
el-container
属性:direction
内部组件:el-header、el-aside、el-main、el-footer
十一、基于Vue的网络框架-vue-axios的应用
入门
安装
npm install --save axios vue-axios
导入
import VueAxios from 'vue-axios'
Vue.createApp({}).use(VueAxios, axios)
使用
this.axios.get(api).then((response)=>{请求结果处理})
其中api需要进行encodeURI编码
vue.config.js配置解决跨域问题
proxy: {
'myApi': {
target: '目标网站'
changeOrigin: true
pathRewrite: {'^/myApi':''}
}
}
'myApi': {
target: '目标网站'
changeOrigin: true
pathRewrite: {'^/myApi':''}
}
}
this.axios.get("/myApi"+"目标网站的路径").then((response)=>{})
使用功能
通过配置方式请求数据
快捷方法
axios.get(url[, config])
axios.post(url[, data[, config]])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.put(url[, config])
axios.patch(url[, data[, config]])
axios.post(url[, data[, config]])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.put(url[, config])
axios.patch(url[, data[, config]])
通用方法
this.axios({
method:"get",
url: "/myApi"+api,
}).then((response)=>{请求结果处理})
method:"get",
url: "/myApi"+api,
}).then((response)=>{请求结果处理})
复用axios请求实例
const instance = this.axios.create({
baseURL: "/myApi",
timeout: 1000,
headers: {通用请求头}
});
baseURL: "/myApi",
timeout: 1000,
headers: {通用请求头}
});
instance.get(api).then((response)=>{请求结果处理})
请求配置冲突合并
axios默认配置 < defaults属性配置 < 请求时的config参数配置
请求的配置和响应数据结构
配置数据结构
url、method、baseURL、data、headers等
响应数据结构
data、status、headers、config、request
拦截器
拦截请求发起前和完成后
全局拦截
拦截请求开始
this.axios.interceptors.request.use(
(config)=>{拦截请求开始return config},
(error)=>{出现错误的拦截处理return Promise.reject(error)}
)
(config)=>{拦截请求开始return config},
(error)=>{出现错误的拦截处理return Promise.reject(error)}
)
拦截请求完成
this.axios.interceptors.response.use(
(response)=>{拦截请求完成return response},
(error)=>{出现错误的拦截处理return Promise.reject(error)}
)
(response)=>{拦截请求完成return response},
(error)=>{出现错误的拦截处理return Promise.reject(error)}
)
全局移除拦截器
this.axios.interceptors.request.eject(requestInterceptor)
特定请求拦截
拦截请求开始
this.axios({
method:"get",
url: "/myApi"+api,
interceptors: {request: myInterceptor}
}).then((response)=>{请求结果处理})
method:"get",
url: "/myApi"+api,
interceptors: {request: myInterceptor}
}).then((response)=>{请求结果处理})
拦截请求完成
略
十二、路由管理
入门
安装
npm install vue-router@4 -s
使用
路由跳转链接
<router-link to="/demo1">页面1</router-lijnk>
路由页面出口
<router-view></router-view>
创建路由
const router = createRouter({
hisotry: createWebHashHistory(),
router:[{path:"/demo1",component: Demo1}]
})
hisotry: createWebHashHistory(),
router:[{path:"/demo1",component: Demo1}]
})
注册路由
Vue.createApp({}).use(router)
带参数的动态路由
路由参数匹配
参数获取
组件内部使用$route属性获取全局的路由对象
路由对象的params属性可以获取定义的参数
路由对象的params属性可以获取定义的参数
{{ $route.params.username }}
参数定义
在路由定义时通过路径变量定义参数
使用冒号标记参数
使用冒号标记参数
[{path:"/user/:username/:id", component: User}]
参数传递
路由跳转时的路由链接指定参数
/user/小王/8888
注:相同组件只会执行一次生命周期
提升组件重用性
提升组件重用性
通过使用导航守卫处理相同组件路由的切换
export default { beforeRouteUpdate(to, from){路由切换处理}}
路由匹配语法规则
参数指定正则
/user/:username
/user/:id(\\d+)
/user/:id(\\d+)
其中id参数的路由只匹配数字,其他参数则匹配username
参数匹配数组
/category/:cat*
/category/一级/二级/三级
cat为数组["一级", "二级", "三级"]
参数可选
/user/:username?
其中可不传递username参数
嵌套路由
父组件预留路由出口
<router-view></router-view>
定义嵌套路由
[{
path: "/user/:username?",
component: User,
children: [
path: "/friends/:count",
component: Friends
]
}]
path: "/user/:username?",
component: User,
children: [
path: "/friends/:count",
component: Friends
]
}]
children的实际路径由父组件和自身path拼接
/user/小王/friends/6
/user/小王/friends/6
不限嵌套层数
Friends组件显示在父组件的router-view组件内
页面导航
使用路由方法
使用$router对象的push方法向history栈添加一个新记录
几种push方法的参数传递方式
this.$router.push({path:"/user/小王"})
this.$router.push("/user/小王")
this.$router.push({name:"user",params:{username:"小王"}})
需要定义路由的时间对路由进行命名
this.$router.push({path:"/user", query:{name: 'xixi'}})
传递路由查询参数
设置了path,params属性会被自动忽略
push方法返回Promise对象
this.$router.push(****).then(()=>{跳转结果处理})
导航历史控制
路由替换
this.$router.push({path:"/user/小王", repleace: true})
this.$router.replace({path:"/user/小王"})
路由跳转
跳转到后一个记录:this.$router.go(1)
跳转到后三个记录:this.$router.go(3)
跳转到前一个记录:this.$router.go(-1)
路由的命名
使用名称进行跳转
push方法方式
this.$router.push({name:"user",params:{username:"小王"}})
router-link方式
<route-link :to="{ name: 'user', params:{username: '小王' }}">小王</router-link>
路由视图命名
在一个组件中包含多个router-view,用name区分
<router-view name="topBar"></router-view>
定义路由时指定每个路由的填充组件
const routes = [{
path: "/home/:username/:id"
components: {
topBar: User,
default: UserSetting
}
}]
path: "/home/:username/:id"
components: {
topBar: User,
default: UserSetting
}
}]
特殊的对于未命名的路由视图,使用default的组件填充
使用别名
const routes = [{
path: "/home/:username/:id"
components: UserSetting,
alias: '/setting/:id'
}]
path: "/home/:username/:id"
components: UserSetting,
alias: '/setting/:id'
}]
必须包含原path中的参数
alias: ['/setting/:id', '/s/:id']
路由重定向
静态路由重定向
const routes = [{
path: "/home/:username/:id"
components: UserSetting,
redirect: '/otherPage'
}]
path: "/home/:username/:id"
components: UserSetting,
redirect: '/otherPage'
}]
redirect: { name: "OtherPage"}
动态路由重定向
const routes = [{
path: "/home/:username/:id"
components: UserSetting,
redirect: to => {return { path: "/otherPage"}}
}]
path: "/home/:username/:id"
components: UserSetting,
redirect: to => {return { path: "/otherPage"}}
}]
通过函数计算得到不同的路由
路由传参
通过$router.params组件与路由强绑定
通用方式:使用外部属性接收路由的参数传递
通用方式:使用外部属性接收路由的参数传递
组件的外部属性定义
选项的props属性
外部属性id
路由参数id
路由参数id
路由的props属性
props是布尔类型
路由参数会自动映射到外部属性
props: true
props是对象
props的数据原样传递给组件的外部属性
props: {id: "000"}
props是函数
函数返回传递给外部属性的对象
props: route => {return {id: router.params.id, name: "小王"}}
路由导航守卫
全局导航守卫
router的beforeEach方法注册全局的前置导航守卫
router.beforeEach((to, from)=>{return true})
参数函数返回值为布尔值时决定是否能跳转
router.beforeEach((to, from)=>{return { name: "setting", params: {id: "0000"}}})
参数函数返回值为路由对象时跳转到指定路由
router的afterEach方法注册全局的后置导航守卫
router.afterEach((to, from, failure) => {后置事件统一处理})
特定路由导航守卫
路由定义时指定
const routes = [{
path: "/home/:username/:id"
components: UserSetting,
beforeEnter: router => {return true}
}]
path: "/home/:username/:id"
components: UserSetting,
beforeEnter: router => {return true}
}]
组件内部定义
export default {
beforeRouteEnter(to, from){通过路由切换到当前组件时调用}
beforeRouteUpdate(to, from){当前路由发生变化时调用}
beforeRouteLeave(to, from){离开当前页面时调用}
}
beforeRouteEnter(to, from){通过路由切换到当前组件时调用}
beforeRouteUpdate(to, from){当前路由发生变化时调用}
beforeRouteLeave(to, from){离开当前页面时调用}
}
beforeRouteEnter不能直接通过this获取当前组件
当前组件未被创建
beforeRouteUpdate、beforeRouteLeave能通过this获取当前组件实例
当前组件未被创建
beforeRouteUpdate、beforeRouteLeave能通过this获取当前组件实例
beforeRouteEnter(to, from, next){
next((w)=>{w为当前组件实例,在确认此次跳转后调用次方法})
return true
}
next((w)=>{w为当前组件实例,在确认此次跳转后调用次方法})
return true
}
通过next参数注册确认此次跳转的回调方法
动态路由
动态添加与删除路由
动态添加路由
let call = this.$router.addRoute({path:"/demo",name:"demo",component:Demo})
添加重复名称的路由会覆盖原路由
addRoute返回删除回调,可通过执行删除回调删除路由
call()
动态删除路由
this.$router.removeRoute("demo")
通过路由名称删除
其他路由的函数
检查是否包含某路由
this.$router.hasRoute("demo")
获取所有路由
this.$router.getRoutes()
十三、Vue状态管理
认识Vuex框架
状态管理
视图触发动作,动作改变状态,状态驱动视图
Vuex:多组件依赖同一状态;多组件变更一个状态
入门
安装
npm install vuex@next --save
使用
导入 import { createStore } from 'vuex'
创建仓库
const store = createStore({
state(){count:0}
mutations:{
increament(state){ state.count++ }
}
})
state(){count:0}
mutations:{
increament(state){ state.count++ }
}
})
注册
instance.use(store)
访问状态
this.$store.state.count
变更状态
this.$store.commit('increment')
介绍
Vuex的store存储的状态是响应式的
store中的状态改变的唯一方法时提交mutation操作
Vuex的核心概念
状态state
访问方式:this.$store.state
通过mapState简化访问
import {mapState} from 'vuex'
{{ mapState(['count']) }}
{{ mapState(['count']) }}
映射访问
通过字符串映射
computed: mapState({
countDate: 'count'
})
countDate: 'count'
})
通过函数映射
computed: mapState({
countDate2(state){
return state.count
}
})
countDate2(state){
return state.count
}
})
Getter方法
mapState:定义在业务逻辑处,将状态映射为计算属性
getters:定义再store中,全局通用的计算属性
createStore({
state(){return****}
mutations:{****}
getters: {
countText(state){ return (s) =>{return state.count + s}}
}
})
state(){return****}
mutations:{****}
getters: {
countText(state){ return (s) =>{return state.count + s}}
}
})
调用
this.$store.getters.countText("次")
通过mapGetters简化访问
import {mapGetters} from 'vux'
映射为组件内计算属性computed: mapGetters(['countText'])
使用 countText('次')
Mutation
无参的mutation
mutations:{
increament(state){ state.count++ }
}
increament(state){ state.count++ }
}
this.$store.commit('increament')
带参数的mutation
mutations:{
increament(state,n){ state.count+=n }
}
increament(state,n){ state.count+=n }
}
this.$store.commit('increament', 3)
带对象参数的mutation
mutations:{
increament(state,payload){ state.count+=payload.count }
}
increament(state,payload){ state.count+=payload.count }
}
this.$store.commit('increament', count:3)
Action
通过提交mutation修改状态是同步的操作
Action通过包装执行mutation实现异步修改状态
使用实例
mutations:{
increament(state,n){ state.count+=n }
},
actions: {
asyncIncrement(context, payload){
setTimeout(() => {context.commit('increment', payload)}, 3000)
}
}
increament(state,n){ state.count+=n }
},
actions: {
asyncIncrement(context, payload){
setTimeout(() => {context.commit('increment', payload)}, 3000)
}
}
context为与store实例有相同的方法和属性的上下文对象
payload为用户参数
this.$store.dispatch('asyncIncrement', {count:2})
传递action及其参数
Module
声明模块状态
const module1 = {
state(){count:0}
mutations:{
increament1(state){ state.count++ }
}
}
state(){count:0}
mutations:{
increament1(state){ state.count++ }
}
}
const module2 = {
state(){count:0}
mutations:{
increament2(state){ state.count++ }
}
}
state(){count:0}
mutations:{
increament2(state){ state.count++ }
}
}
创建多module的store
createStore({
modules: {Module1: module1, Module2: module2}
})
modules: {Module1: module1, Module2: module2}
})
访问各module的数据
this.$store.Module1.count
变更状态
this.$store.commit('increament1')
通过命名空间隔离mutation
const module1 = {
namespace: true
state(){count:0}
mutations:{
increament1(state){ state.count++ }
}
}
namespace: true
state(){count:0}
mutations:{
increament1(state){ state.count++ }
}
}
必须加命令空间才能修改状态
this.$store.commit{'Module1/increment1'}
动态注册module
store.registerModule('moduleName', module3)
拓展阅读:Vue3的状态管理Pinia
入门
pinia是vue3官方提供的状态管理库
使用
安装
npm install pinia
注册
const pinia = createPinia()
instance.use(pinia)
instance.use(pinia)
定义store
import { defineStore } from 'pinia'
方式1:使用Option对象定义
const useCounterStore = defineStore('counter', {
state() => ({ count: 0 }),
getters: { double: (state) => state.count*2,}
actions: { increment(){ this.count++ }}
})
state() => ({ count: 0 }),
getters: { double: (state) => state.count*2,}
actions: { increment(){ this.count++ }}
})
方式2:使用setup函数定义
cons useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment(){count.value++}
return { count, increment }
})
const count = ref(0)
function increment(){count.value++}
return { count, increment }
})
创建store实例
setup() {
const store = useCounterStore()
return { store }
}
const store = useCounterStore()
return { store }
}
解决解构导致失去响应式
const store = useCounterStore()
// ❌ 这将无法生效,属性和计算属性都将失去响应性
const { name, doubleCount } = store
// ❌ 这将无法生效,属性和计算属性都将失去响应性
const { name, doubleCount } = store
// ✓名为 increment 的 action 可以直接提取
const { increment } = store
const { increment } = store
storeToRefs()为响应式属性创建引用
const store = useCounterStore()
// `name` and `doubleCount` 都是响应式 refs
// 这也将为由插件添加的属性创建 refs
// 同时会跳过任何 action 或非响应式(非 ref/响应式)属性
const { name, doubleCount } = storeToRefs(store)
// `name` and `doubleCount` 都是响应式 refs
// 这也将为由插件添加的属性创建 refs
// 同时会跳过任何 action 或非响应式(非 ref/响应式)属性
const { name, doubleCount } = storeToRefs(store)
State
定义为一个返回初始状态的函数
通过Store实例访问State:可读写
通过Store实例访问
const store = useStore()
store.count++
store.count++
重置State
使用Store的$reset方法重置为初始值
store.$reset()
通过mapState访问:仅可读
import { mapState } from 'pinia'
state只读映射
mapState(Store定义, 读取数据方式)
通过字符串定义,注册名称为访问属性名称
mapState(useCountStore, ['count'])
通过函数定义映射,指定注册名称
mapState(useCountStore, {
double: store =>store.count * 2
// 可通过this访问
magicValue(store){ return store.someGetter * this.count * this.double }
})
double: store =>store.count * 2
// 可通过this访问
magicValue(store){ return store.someGetter * this.count * this.double }
})
可修改的state
mapWritableState(store定义, 数据读取方式)
通过字符串定义,注册名称为访问属性名
mapWritableState(useCountStore, ['count'])
指定注册名称
mapWritableState(useCountStore, {
myOwnName" 'count',
})
myOwnName" 'count',
})
变更State
直接访问单个属性
store.count++
使用$path函数一次变更多个属性
store.$patch({
count: store.count + 1
age: 20,
name: "DIO",
})
count: store.count + 1
age: 20,
name: "DIO",
})
使用$patch函数变更属性内部数据
手动触发数据更新
手动触发数据更新
store.$patch{(state) => {
state.people.name = '张三'
state.hasChanged = true
}}
state.people.name = '张三'
state.hasChanged = true
}}
替换state
不能进行的操作
❌store.$store = {count:24}// 破坏响应性
使用$patch函数完整替换state
store.$patch({count:24})
通过pinia实例的state设置整个应用的初始state
pinia.state.value = {}
订阅state
订阅store的数据变更
store.$subscrible((mutation, state) => {处理逻辑})
mutation为变更事件相关,state为变更前的状态
完整监听pinia实例的state
watch(pinia.state, (state) =>{处理逻辑}, {deep: true})
Getter
返回值
getters: {
doubleCount: (state) => state.count * 2
}
doubleCount: (state) => state.count * 2
}
返回函数
gettres: {
getUserById: (state)=>{
return (userId) => f(userId)
}
}
getUserById: (state)=>{
return (userId) => f(userId)
}
}
使用getter并传递参数
setup(){return { getUserById: useStore().getUserById }}
<template><p> {{ getUserById(2) }}</p></template>
Action
异步执行、相当于组件中的method
actions: {
increment() {
this.count++
},
}
increment() {
this.count++
},
}
actions: {
async requestServer() {
await api.post()
},
}
async requestServer() {
await api.post()
},
}
订阅action
const unsubscribe = someStore.$onAction(
({
name, // action 名称
store, // store 实例,类似 `someStore`
args, // 传递给 action 的参数数组
after, // 在 action 返回或解决后的钩子
onError, // action 抛出或拒绝的钩子
}) => {订阅实际逻辑}
)
({
name, // action 名称
store, // store 实例,类似 `someStore`
args, // 传递给 action 的参数数组
after, // 在 action 返回或解决后的钩子
onError, // action 抛出或拒绝的钩子
}) => {订阅实际逻辑}
)
action绑定的组件卸载时会自动删除所有action订阅器
通过设置第二个参数为true分离组件和订阅器
通过设置第二个参数为true分离组件和订阅器
someStore.$onAction(callback, true)
unsubscribe() //手动删除监听器
其他高级应用
插件
https://pinia.vuejs.org/zh/core-concepts/plugins.html
SSR
一、前端基础到Vue.js
HTML基础
CSS基础
选择器
通用选择器*、标签选择器、类选择器、id选择器
CSS样式入门
背景、样式、边框与边距
JS基础
语法
变量:var、let;常量:const
表达式:数据类型、运算符
函数定义
条件分支:if-else、switch、while
Vue基础
data()、methods()
Vue.createApp().mount("#id")
新特性:性能、typescript支持
对比AngularJs:入门简单、灵活性强、轻量、API简单
二、模板应用
模板基础
模板插值
{{ 值表达式 }}
{{ count }}、{{ count + 10 }}
仅插值一次:v-once
<h1 v-once>{{ count }}</h1>
HTML插值:v-html
<span v-html="htmlVar"></span>
属性插值:v-bind
<h1 v-bind:id="id1">内容</h1>
模板指令
模板指令也是HTML标签属性:通常以前缀v-开头
指令及指令参数
v-指令[.修饰符][:参数]
v-bind:style、v-on:click
参数动态化
v-bind:[prop]="classVar"
参数修饰符
v-model.trim="content"
常用指令的简化
v-bind:id=id简化:id="id"
v-on:click="btnClick"简化@click="btnClick"
条件渲染
v-if
v-if指令条件渲染
v-if="条件表达式"
<h1 v-if="show">标题</h1>
v-else指令
必须紧跟v-if指令
<h1 v-if="show">标题</h1>
<h1 v-else>标题2</h1>
<h1 v-else>标题2</h1>
v-else-if指令
v-else-if="条件表达式"
<h1 v-if="score ==100">满分</h1>
<h1 v-else-if="score > 60">优秀</h1>
<h1 v-else>不及格</h1>
<h1 v-else-if="score > 60">优秀</h1>
<h1 v-else>不及格</h1>
div包装的v-if、template一组的v-if
v-show
v-show指令条件渲染
v-show="条件表达式"
<h1 v-show="show">标题</h1>
v-if对比v-show
v-if
懒加载、条件加载重组和销毁:切换性能消耗大
v-show
display:none:初始渲染性能消耗
循环渲染
v-for
v-for循环渲染指令
v-for="变量名 in 集合名"
v-for="item in list"
v-for="(变量名,索引) in 集合名"
<ul>
<li v-for="(item,index) in list">
<div>{{ item.name }} - {{ item.id }}</div>
</li>
</ul>
<li v-for="(item,index) in list">
<div>{{ item.name }} - {{ item.id }}</div>
</li>
</ul>
v-for="(变量名,key,索引) in 集合名" :key="index"
指定主键
v-for高级用法
v-for对列表进行循环渲染-》对数据对象的绑定
通过列表数据进行处理后循环渲染
v-for="item in handle(list)"
三、Vue组件的属性和方法
属性和方法
属性:data函数
通过组件实例获取:instance.count
通过$data获取:instance.$data.count
方法:methods选项
自动绑定方法到组件实例本身,可使用this关键字访问属性
计算属性和侦听器
计算属性:computed选项
computed: {
type() { return this.count > 80 ? "合格" : "不合格" }
}
type() { return this.count > 80 ? "合格" : "不合格" }
}
访问
读取:指定get方法
computed: {
type{
get(){ return **** }
}
}
type{
get(){ return **** }
}
}
console.log(instance.type)
写入:指定set方法
computed: {
type{
set(newValue){ this.****= }
}
}
type{
set(newValue){ this.****= }
}
}
instance.type=****
计算属性还是函数
计算属性:缓存计算结果,依赖属性没变化不会重新执行
函数:每次访问都会重新执行函数的逻辑
属性侦听器
监听属性的变化:通过watch选项
watch: {
count(oldValue, newValue){
****
}
}
count(oldValue, newValue){
****
}
}
函数限流
限流:限制操作,常见的方案时根据事件间隔进行限流,即在指定事件间隔内不允许重复执行同一函数
手动实现限流函数
通过setTimeout函数延迟设置信号量
使用Lodash库进行限流
引用
lodash.min.js
使用
click: _.debounce(func(){ **** }, 2000)
表单数据双向绑定
文本框
<input v-modle="变量名"/>
多行文本框
<textarea v-model="变量名"></textarea>
不能直接插入文本
<textarea v-model="变量名">{{ text}}</textarea>
复选框与单选框
复选框
<input type="checkbox" v -model="checkbox"/>
单选框
<input type="radio" value="男" v -model="sex"/>
选择列表
单选选择列表
<select v-model="select">
<option>选项1</option>
****
</select>
<option>选项1</option>
****
</select>
多选选择列表
<select v-model="select" multiple>
<option>选项1</option>
****
</select>
<option>选项1</option>
****
</select>
绑定指令修饰符
lazy
不实时同步属性的值
v-model.lazy="text"
trim
将绑定的文本数据的首尾空格去掉
v-model.trim="text"
样式绑定
可行方案:通过class属性、id属性或直接使用标签名进行CSS样式绑定
class属性绑定
内联方式绑定
:class="{类名:条件表达式,...}"
<div :class="{blue:isblue,red:isRed}">
组件数据对象方式绑定
:class="变量名"
<div :class="style"></div>
style:{blue:true,red:flase}
style:{blue:true,red:flase}
数组对象绑定
:class="[变量名1,变量名2]"
<div :class="[blueClass, redClass]"></div>
blueClass:"blue"
redClass:"red"
blueClass:"blue"
redClass:"red"
绑定内联样式
通过style属性设置,样式采用驼峰
<div :style="{color: 变量名,fontSize:变量名}"></div>
四、处理用户交互
事件的监听与处理
事件监听示例
v-on:click="方法或函数体"
通常使用@click代替
click(用户自定义参数, [event]){}
自动获取Event对象
多事件处理
@click="func1(), func2()"
事件捕获与事件冒泡
事件捕获:从父组件向子组件传播
事件冒泡:从子组件向父组件传播
@click默认监听事件冒泡
事件修饰符
@click.capture
监听事件捕获阶段
@click.stop
阻止事件传递
@click.once
只触发一次事件
@click.self
当事件对象的target属性是当前组件时才触发事件
@click.Prevent
禁止默认的事件
@click.passive
不禁止默认的事件
事件类型
常用事件类型
click、dblclick
focus、blur、change、select
mousedown、mouseup、mousemove、mouseout、mouseover
keydown、keyup
focus、blur、change、select
mousedown、mouseup、mousemove、mouseout、mouseover
keydown、keyup
按键修饰符
组合事件和修饰符实现特定功能
@keyup.enter="keyup"
@mousedown.alt.ctrl="mousedown"
键盘按键修饰符:enter、page-down、ctrl、alt、shift、meta等
鼠标按键修饰符:left、right、middle
五、组件基础
Vue应用与组件
数据配置选项
Vue.createApp(数据配置选项)
创建的实例通过mount方法绑定到指定的HTML元素上
data:函数,提供应用所需的全局数据
props:用于接收父组件传递的数据
computed:配置组件的计算属性
methods:配置组件使用的方法
不要使用箭头函数,会影响this关键字的使用
watch:对组件属性的变化添加监听函数
可以直接引用methods中定义的函数
定义组件
使用component方法定义组件
const alertComponent = {
data() {****}
methods: {****}
template: `<div><button @click="click">按钮</button></div>`
}
data() {****}
methods: {****}
template: `<div><button @click="click">按钮</button></div>`
}
instance.component("组件名",组件配置)
instance.component("my-alert",alertComponent)
使用组件
<my-alert></my-alert>
每一个标签都是一个独立的组件实例,内部数据独立维护
组件中的数据与事件的传递
组件添加外部属性
组件外部属性定义
const alertComponent = {
props: ["title"],
template: `<div><button @click="click">{{ title }}</button></div>`
}
props: ["title"],
template: `<div><button @click="click">{{ title }}</button></div>`
}
使用
<my-alert title="按钮"></my-alert>
处理组件事件
方式1:组件内使用内建的$emit方法传递事件
template: `<div><button @click="$emit(myclick)">按钮</button></div>`
方式2:组件内使用内建的$emit方法同时传递事件和数据
@click="$emit('myclick', title)"
方式3:对事件进行包装
组件模板定义@click="click"
组件方法定义click(){this.$emit('myclick', this.title)}
组件方法定义click(){this.$emit('myclick', this.title)}
组件实例使用参数声明事件
<my-aliert @myclick="clickfunc" title="按钮"></my-alert>
组件使用v-model
通用组件数据双向绑定
方式1:v-model指令
方式2::value+@input绑定结合事件处理
自定义组件数据双向绑定
组件实例使用v-model指定参数
<my-input v-model="inputText"></my-input>
组件定义使用:value和@input组合实现数据绑定
props: ["modelValue"]
<input :value="modelValue" @input="action"/>
<input :value="modelValue" @input="action"/>
自定义组件的插槽
插槽:HTML起始标签和结束标签中间的部分
自定义插槽
基本用法
使用slot标签来指定插槽的位置
template: `<div><slot></slot></div>`
使用插槽
<my-component>插槽位置显示</my-component>
具名插槽:给插槽定义名字
slot标签的name属性指定插槽名称
template: `<div><slot name="name1"></slot><slot name="name2"></slot></div>`
使用具名插槽
v-slot指令
<my-component>
<template v-slot="name1">插槽name1内容</template>
<template v-slot="name2">插槽name2内容</template>
</my-component>
<template v-slot="name1">插槽name1内容</template>
<template v-slot="name2">插槽name2内容</template>
</my-component>
使用符号#代替v-slot
<my-component>
<template #name1>插槽name1内容</template>
<template #name2>插槽name2内容</template>
</my-component>
<template #name1>插槽name1内容</template>
<template #name2>插槽name2内容</template>
</my-component>
动态组件
使用component标签结合is属性动态渲染组件
<component :is="currentComponent"></component>
六、组件进阶
组件的生命周期
生命周期方法
beforeCreate:组件即将创建
created:组件创建完成
beforeMount:组件即将挂载前
mounted:组件挂载完成
beforeUpdate:组件即将更新前
updated:组件更新完成
activated:被缓存的组件激活时调用
deactivated:被缓存的组件停用时调用
beforeUnmount:组件即将被卸载前调用
unmounted:组件被卸载后调用
errorCaptured:捕获到来自子组件的异常时调用
renderTracked:虚拟DOM重新渲染时调用
renderTriggered:虚拟DOM被触发渲染时调用
created:组件创建完成
beforeMount:组件即将挂载前
mounted:组件挂载完成
beforeUpdate:组件即将更新前
updated:组件更新完成
activated:被缓存的组件激活时调用
deactivated:被缓存的组件停用时调用
beforeUnmount:组件即将被卸载前调用
unmounted:组件被卸载后调用
errorCaptured:捕获到来自子组件的异常时调用
renderTracked:虚拟DOM重新渲染时调用
renderTriggered:虚拟DOM被触发渲染时调用
应用的全局配置选项
自定义异常和警告捕获
instance.config.errorHandler = (err, vm, info) => {}
instance.config.warnHandler = (msg, vm, trace) => {}
属性
内部使用的属性
在内部data函数中定义
props外部属性
全局属性
instance.config.globalProperties = {version: "1.0",***}
组件的注册方式
全局注册
instance.component("my-component", my-component)
局部注册
在组件的配置中使用components属性局部注册
components: {"my-component", my-component}
组件props属性高级用法
对props属性进行验证
对如下内容添加约束进行配置
1、类型
2、默认值
3、是否选填
1、类型
2、默认值
3、是否选填
通用定义
props:{
count: {type: Number,required:false,default:10}
}
count: {type: Number,required:false,default:10}
}
类型约束
类型多选:count:[Number,String]
仅设置属性类型可以通过快速定义多个
props:{
count:Number,
count2:String,
}
props:{
count:Number,
count2:String,
}
默认值约束
具体值方式指定:default:值
函数方式指定:default: func(){return 10;}
props的只读属性
props属性只读性能是Vue单向数据流特性的一种体现
所有外部属性Props都只允许父组件的数据流动到子组件
解决只读属性:通过内部属性定义接收外部属性
组件数据注入
数据注入时一种便捷的组件间数据传递方式
避免逐层传递
使用provide和inject配置项
父组件provide配置项提供数据
provide(){return{listCount: this.count}}
子组件inject配置项获取数据
inject:['listCount']
provide配置项冲突的处理:子组件就近使用父组件提供的数据
组件Mixin技术
使用Mixin定义组件
定义Mixin通用配置
const myMixin = {props:['title']}
在组件中使用通用配置
{mixins: [myMixin],template:***}
Mixin选项的合并
属性冲突的合并:以内部定义为准
生命周期函数的合并:先触发Minxin的实现, 后触发内部定义的实现
全局Mixin
instance.mixin({配置选项})
使用自定义指令
使用自定义指令
instance的directive方法可以注册全局的自定义指令
instance.directive("getfocus",{mounted(element){}})
使用时在指令名称前加v-
<input v-getfocus />
自定义指令的其他生命周期
mounted、beforeMount、beforeUpdate、updated、beforeUnmounted、unmounted
自定义指令参数
通过param对象传递指令中的参数
instance.directive("getfocus",{mounted(element,param){
param.arg和param.value
}})
param.arg和param.value
}})
再指令后更参数和值
<input v-getfocus:custum="1" />
传递对象<input v-getfocus:custum="{a:1,b:2}" />
组件的Teleport功能
由于树结构改变影响组件内元素的布局
传统实现:拆分布局保证组件挂载在目标标签下
通过teleport实现
在template中定义挂载的元素
<teleport to="body"> 需要挂载的内容</teleport>
七、Vue响应式编程
响应式编程原理及应用
手动跟踪变量变化
Proxy对原对象进行包装:实现对对象属性设置及获取的监听
定义目标对象代理
let proxy = new Proxy(target,handler)
handler = {
set(target,key,value){target[key]=value;其他设置的时候的操作}
get(target,prop){读取时的设置;return target[prop]}
}
set(target,key,value){target[key]=value;其他设置的时候的操作}
get(target,prop){读取时的设置;return target[prop]}
}
使用代理对象进行数据读取与写入(同时指定handler对于读取和写入的逻辑)
读取和写入proxy.字段
Vue中的响应式对象
setup方法:
组件被创建前定义组件需要的数据和函数
组件被创建前定义组件需要的数据和函数
setup() {let myData = {value:0}; function click(){/**/};return{myData,click};}
setup返回的是普通对象
通过reactive方法对自定义对象进行包装添加响应式
let myData = Vue.reactive({value:0})
data返回的会被包装为Proxy对象
独立的响应式值Ref的应用
ref:对非对象进行响应式包装
Proxy、reactive将对象进行响应式包装,独立的原始值采用ref方法进行包装
let myObject = Vue.reactive(object)
let numberProxy = Vue.ref(0)
let numberProxy = Vue.ref(0)
写入是使用:numberProxy.value写入
toRefs:抽取对象的数据进行响应式包装
JavaScript原始解构,但无响应式特性
let {value} = myObject
解构后失去响应式特性
Vue提供的方式抽取数据,返回ref对象
let {value} = Vue.toRefs(myObject)
响应式的计算与监听
计算变量
组件中使用computed选项
同名方法创建计算变量
定义
let sum = Vue.computed({
set(value){设置的逻辑}
get(){return 受其他变量影响的数值函数}
})
set(value){设置的逻辑}
get(){return 受其他变量影响的数值函数}
})
使用
sum.value = 0;
console.log(sum.value)
console.log(sum.value)
响应式变量
watchEffect
对方法中的所有响应式变量进行监听,有变更则触发方法调用
样例
let a = Vue.ref(0)
Vue.watchEffect(() =>{逻辑代码console.log(a.value)})
Vue.watchEffect(() =>{逻辑代码console.log(a.value)})
生命周期:watchEffect在setup方法中被调用后,和组件生命周期绑定
watch与watchEffect对比
watch
可以获取变化前后的值
可以同时监听多个值的变化
可以获取变化前后的值
可以同时监听多个值的变化
Vue.watch(()=>{return a.data}, (value,old=>{变更的处理}))
Vue.watch(b, (value,old)=>{变更的处理})
监听多个值:Vue.watch([a,b], ([valueA,valueB],[oldA,oldB])=>{})
组合式API的应用
setup方法
组合式API功能的入口,在组件创建之前执行(beforeCreate方法之前)
能访问:props
不能使用this访问组件的其他属性
setup方法可接收的参数:props和context
props
响应式的外部参数对象
context
JavaScript对象,含attrs、slots和emit
setup方法可返回的对象
对象包装的数据可以再组件的其他选项或HTML模板中使用
如果返回渲染函数:可替代template定义模板
return () => Vue.h('div', [data])
方法中定义生命周期
onBeforeMount、onMounted
onBeforeUpdate、onUpdated、onBeforeUnmount、onUnmounted
onErrorCaptured、onRenderTracked、onRenderTriggered
onBeforeUpdate、onUpdated、onBeforeUnmount、onUnmounted
onErrorCaptured、onRenderTracked、onRenderTriggered
在原生命周期前加on、去掉了beforeCreate和created
setup(){
Vue.onMounted(()=>{绑定时的代码逻辑})
}
Vue.onMounted(()=>{绑定时的代码逻辑})
}
组件的生命周期函数和setup生命周期方法冲突
先调用setup再调用组件内部定义
0 条评论
下一页