安装项目
安装脚手架
使用可视化界面创建
$ vue ui
按需安装即可
安装完关掉就好
VSCode 的打开项目
安装依赖
$ npm i
运行项目
使用命令
$ npm run serve
配置路由
勾选上配置默认是配好的,按需更改就好
vuex
勾选上配置默认是配好的,按需更改就好
模版语言
使用 插入属性
<template>
<div>
{{ msg }}
<!-- 路由视图容器 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
// 生命周期 创建后
created() {
console.log('App.vue created');
},
// 生命周期 渲染后
mounted() {
console.log('App.vue mounted');
},
// 属性
data() {
return {
msg: 'hello world',
};
},
// 方法
methods: {},
// 计算属性
computed: {},
// 监听属性
watch: {},
// filters 过滤器
filters: {},
};
</script>
<style scoped></style>
选项式 API
<script>
export default {
// 属性
data() {
return {
msg: 'hello world',
};
},
// 方法
methods: {},
// 计算属性
computed: {},
// 监听属性
watch: {},
// filters 过滤器
filters: {},
// 自定义指令的 directives
directives: {},
};
</script>
CSS 引入
全局引入
1. 编写全局样式文件 如 global.css
2. 在main.js文件中引入 该css文件
局部引入
在模版中编写
<style scoped lang="less"></style>
组件 CSS
1. 组件内使用的CSS
<style>
.my-component {
background-color: #fff;
padding: 20px;
}
</style>
生命周期
目录
vue2 的生命周期
方法 | 作用 |
---|---|
beforeCreate() | 在创建之前调用,这时候数据、方法、Dom 都未创建无法访问 |
created() | 在创建之后调用,这个时候可以访问数据、方法、计算属性等 |
beforeMount() | 挂载之前调用,dom 挂载阶段之前,此时组件的 template 已经解析完成,但还未生成真正的 DOM 节点。 |
mounted() | 挂载之后调用,这时候的 Dom 树已经构建完成,可以对 DOM 结构进行操作 |
beforeUpdate() | 在更新之前调用,可以在这里进一步改变数据。注意避免进入死循环的情况 |
updated() | 在更新之后调用,在这里可以获取到更新后的 DOM 元素的状态 |
beforeDestroy() | 在组件被销毁之前调用,此时组件实例依旧可以正常访问,可以进行一些清理工作 |
destroyed() | 在组件销毁之后调用,组件实例已经被清理掉了,事件监听等已经被清除 |
activated() | 在组件被激活时触发,keep-alive |
deactivated() | 在组件被缓存时触发,keep-alive |
errorCaptured()【er rou--ka pu to er de】 | (Vue2.5 版本引入的)用来捕获子组件的渲染错误和无法被 try-catch 捕获到的错误 |
serverPrefetch | ssr 渲染的场景 |
beforeCreate
1. 创建阶段在实例化之前调用
2. 此阶段的属性和方法还未实例化,DOM结构也没有渲染。故此数据对象,DOM都无法调用
3.$el和$data属性也还不存在
4. 应用: 初始化属性
Created
1. 实例化之后调用
2. 此时可以调用数据对象,属性和方法。但是无法访问DOM结构
3. 如果想在次阶段操作DOM可以通过 异步实现该操作。
4. 在此处进行请求数据(axios)可以在子组件之前获取数据,此时子组件还未创建,没有进入子组件的生命周期
beforeMount
1. 挂载之前调用,此时组件的 模版(template) 已经解析完成,编译成了虚拟DOM,但是DOM还没有渲染成真正的DOM,这个阶段可以操作虚拟DOM。
2. 模板已经编译成了虚拟 DOM
3. 此阶段可以请求数据,弹药注意
Mounted
1. 挂载之后调用,此时DOM结构创建以及渲染完成,在这里可以获取到DOM结构,对DOM结构进行操作。
2. 此时数据对象(属性和方法),DOM结构都已经可以访问了
3. 在这个阶段子组件已经创建完成,子组件的生命周期已经走完前四步
4. 在这个阶段请求数据(axios)可以再子组件之后获取数据,应用场景是子父
5. 在Mounted中修改DOM
beforeUpdate
1. 更新阶段===> beforeUpdate && updated
2. 此阶段在数据产生变化时,在实际更新前触发该生命周期函数,可以对数据进行进一步修改。
Updated
1. 数据更新之后调用
2. 这个阶段数据更新已经完成,获取到更新之后的数据
beforeDestroy
destroyed
activated
1. 在组件被激活时触发,
2. 应用:详情页面请求数据
1. 在activated中判断id是否相同,相同不再发送http请求
deactivated
1. 在组件被缓存时触发'
会将组件添加到缓存队列中。
代码示例
<template>
<div ref="myLife">
我是生命周期页面
<div>{{ getAge }}</div>
<button @click="age = 27">改变年龄</button>
<button @click="delDomElement">删除DOM元素</button>
</div>
</template>
<script>
export default {
// 创建阶段 属性和方法创建之前
beforeCreate() {
// 在实例刚被创建之初,组件的数据,属性和方法的初始化还没有开始,DOM还没有生成
console.log('我是beforeCreate');
console.log('数据', this.msg); // undefined
console.log('计算属性', this.getAge); // undefined
console.log('方法', this.getFunc); // undefined
console.log('Dom结构', this.$refs.myLife); // undefined
console.log('=============================');
},
created() {
//实例已经完全创建成功,此时可以访问数据、计算属性,方法,还没有开始挂载和渲染DOm结构
console.log('我是created');
console.log('数据', this.msg); // ture
console.log('计算属性', this.getAge); // true
console.log('方法', this.getFunc()); // true
console.log('Dom结构', this.$refs.myLife); // undefined
console.log('=============================');
},
// 挂载阶段 dom挂载阶段
beforeMount() {
// 在组件挂载到页面之前,此时组件的 template 已经解析完成,但还未生成真正的 DOM 节点。
console.log('我是beforeMount');
console.log('数据', this.msg); // true
console.log('计算属性', this.getAge); //true
console.log('方法', this.getFunc()); //true
console.log('Dom结构', this.$refs.myLife); // undefined
console.log('=============================');
},
mounted() {
// 组件已经挂载到页面上,此时组件的 DOM 树已经构建完成,可以对组件的 DOM 结构进行操作。
console.log('我是mounted');
console.log('数据', this.msg); //true
console.log('计算属性', this.getAge); //true
console.log('方法', this.getFunc()); //true
console.log('Dom结构', this.$refs.myLife); // true
console.log('=============================');
},
// 更新阶段
beforeUpdate() {
// 在 数据和组件 更新之前被调用 --,可以在这个钩子函数中进一步改变数据
console.log('我是beforeUpdate');
console.log('数据.年龄', this.age);
console.log('计算属性', this.getAge);
},
updated() {
// 在数据和组件 更新之后执行 -可以在这里获取准确的更新后 - 的数据或DOM元素状态
console.log('我是updated');
console.log('数据.年龄', this.age);
console.log('计算属性', this.getAge);
},
// 销毁阶段
beforeDestroy() {
// 在组件被销毁之前调用,此时组件实例仍然完全可用
console.log('我是beforeUnmount');
console.log('DOM', this.$refs.myLife);
},
destroyed() {
// 在组件被销毁后调用,这个阶段组件实例已经被销毁了,该钩子函数中的事件监听等属性和方法以及被清理掉了
console.log('我是unmounted');
console.log('DOM', this.$refs.myLife);
},
// keepAlive
//在组件被缓存起来时触发,用于在组件被缓存起来后执行操作。
deactivated() {
console.log('我是activated');
},
//在组件被激活时触发,需要配合 keep-alive 组件使用,用于在组件被重新激活时执行操作。
activated() {
console.log('我是deactivated');
},
// 捕获错误时调用
errorCaptured(err, vm, info) {
console.log('我是errorCaptured');
console.error(`Error caught in ${vm.$options.name} component:\n ${err}`);
console.error(info);
return false;
},
data() {
return {
msg: 'hello world',
name: '钟明楼',
age: 26,
};
},
computed: {
getAge() {
return this.age * 2;
},
},
methods: {
getFunc() {
return '我是方法';
},
delDomElement() {
// 删除DOM元素
this.$refs.myLife.removeChild(this.$refs.myLife.querySelector('div'));
},
},
};
</script>
组件
组件化
1. 固定组件 2. 子路由动态组件 3. v-if控制组件的渲染 4.
固定组件
局部引入
// 组件内部
// 导入的组件
import Home from './components/HomeView';
import About from './components/AboutView';
export default {
// 局部引入
components: {
Home,
About,
},
};
全局引入
// 全局文件引入组件
import Home from './components/HomeView';
import About from './components/AboutView';
import RefView from './components/RefView';
// 进行全局注册
// 单个
Vue.component('Home', Home);
// 批量
Vue.component({
About: About,
RefView: RefView,
});
动态组件 --- 使用子路由
<!-- 子路由动态加载的路由组件 -->
// router.js
import { createRouter, createWebHistory } from 'vue-router';
const Dashboard = () => import('./views/Dashboard.vue');
const Profile = () => import('./views/Profile.vue');
const Settings = () => import('./views/Settings.vue');
const routes = [
{
path: '/dashboard',
component: Dashboard,
children: [
{
path: 'profile',
component: Profile
},
{
path: 'settings',
component: Settings
}
]
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
组件传参
父子传参
父组件 在子组件标签中 传递参数
<template>
<div>
<Home
:msg="msg"
:name="name"></Home>
</div>
</template>
export default { data() { return { msg: 'hello world', name: '钟明楼' } }, }
子组件 props 接收
export default {
props: {
msg: String,
name: {
// 完整写法
type: String,
default: 'hello world',
required: true,
},
},
data() {
return {};
},
};
子父传参
子组件 $emit
<div>
<button @click="postChild('子组件修改后')">修改父组件Child</button>
</div>
<script>
export default {
methods: {
postChild(val) {
this.$emit('postChild', val)
}
},
}
</script>
父组件通过 在子组件标签中 @xxx 进行接收 --- 自定义事件
<template>
<div>
<div>Child的值为:{{ Child }}</div>
<About @postChild="postChild"></About>
</div>
</template>
export default {
methods: {
// 子组件传值
postChild(val) {
this.Child = val
}
}, data() {
return {
msg: 'hello world',
name: '钟明楼',
Child: '子组件修改前',
}
},
// 方法
methods: {
// 子组件传值
postChild(val) {
this.Child = val
}
},
}
组件双向绑定 -- v-model
1. 父组件在子组件标签中使用 v-model 绑定数据
$ <Child v-model="name" />
2. 子组件中通过props进行接收 固定值为value。 无法更改
$ props: ['value']
3. 子组件通过计算属性(computed)使用数据 固定格式名无法更改
$ computed: {
childValue: {
get() {
return this.value
},
set(val) {
this.$emit('input', val);
},
},
},
4. 标签中直接v-model绑定 childValue
$ <input type="text" v-model="childValue" />
实现父组件和子组件之间的双向数据绑定
<!-- 父组件 -->
<template>
<div>
<h1>组件传参</h1>
<div>{{ name }}</div>
v-model传参:<input
type="text"
v-model="name" />
<Child :name.sycn="name" />
</div>
</template>
<script>
import Child from '../components/Assembly/Parent';
export default {
components: {
Child,
},
data() {
return {
name: '明楼',
};
},
};
</script>
子组件 ===> 规定格式 value + 计算属性 childValue 改不了
<!-- 子组件 -->
<template>
<div>
<h2>v-model传参</h2>
<input
type="text"
v-model="childValue" />
<div>{{ childValue }}</div>
</div>
</template>
<script>
export default {
props: ['value'],
computed: {
childValue: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
},
},
},
};
</script>
组件双向绑定 -- v-bind
1. 使用 v-bind.sync向子组件标签传递数据
$ <Child :AgeProp.sync=age />
2. 子组中通过自定义事件名
$ props:["newAge"]
3. 子组件中通过 :value 进行数据绑定,@input事件完成对数据的修改
$ <input type="text" :value="newAge" @input="bindAge">
4. 修改的事件 ===> 固定emit事件名 update:xxx
$ bindAge(event) {
this.$emit('update:newAge', event.target.value)
}
ref
父组件直接通过 ref 访问 子组件
this.$refs. ref 的名称 .调用的子组件方法()
<template>
<div>
<RefView ref="myChild"></RefView>
<div>父Ref访问子方法:{{ RefString }}</div>
<button @click="callChildMethod">父组件通过refs 调用子方法</button>
</div>
</template>
<script>
export default {
methods: {
// $refs 调用子组件的方法
callChildMethod() {
console.log(1);
this.RefString = this.$refs.myChild.refView() // 调用子组件的方法
}
},
}
</script>
<style></style>
子组件 -- 被访问的方法
<template>
<h1> 我是 Ref 父访子</h1>
</template>
<script>
export default {
methods: {
refView() {
return '我是Ref子组件的方法,我被获取了'
}
},
}
</script>
<style></style>
$Bus 全局事件总线
utils / Bus / index.js
import vue from 'vue';
export const EventBus = new vue();
组件 $emit 组件触发 按钮 传递给 其他组件
EventBus.$emit('注册事件监听名', ‘传递的数据’)
<template>
<div>
<button @click="getEventBus"> 子组件传递参数</button>
</div>
</template>
<script>
// 引入 全局文件 Bus.js
import { EventBus } from '@/utils/Bus'
export default {
methods: {
// bus子组件传值
getEventBus() {
EventBus.$emit('getEventBus', '子组件')
}
},
}
</script>
接收组件通过 在 created 或 mounted
EventBus.$on(‘事件监听器名’,(传递的数据)=>{})
<template>
<div>
<BusView></BusView>
<div>父Bus访问子方法:{{ BusSring }}</div>
</div>
</template>
<script>
// 引入 全局文件 Bus.js
import { EventBus } from '@/utils/Bus'
export default {
created() {
EventBus.$on('getEventBus', (message) => {
console.log('接收到的消息:', message)
this.BusSring = message
// 对消息进行处理等操作
})
},
}
</script>
Provide/inject -- 注入
跨层级传参 祖孙 父子
祖
export default {
// provide注入 跨层级传参
provide: {
provideString: "我是Provide注入的数据",
ProvideFunc: () => {
console.log('我是Provide注入的方法');
}
},
},
孙 inject 接收注入的数据
export default {
// 简写
inject: ['provideString', 'ProvideFunc'],
// 完整写法
inject: {
// 属性
provideString: {
from: 'user',
default: '我是默认值'
},
// 方法
ProvideFunc: {
from: 'ProvideFunc',
default: null
}
},
props: {},
data() {
return {}
},
},
vuex
见下文
路由
配置路由
import Vue from 'vue';
import VueRouter from 'vue-router';
import HomeView from '../components/HomeView.vue';
Vue.use(VueRouter);
const routes = [
{
path: '/',
name: 'home',
component: HomeView,
// 子路由 配合 <router-view/> 使用
children: [
{
path: 'about',
component: () => import('../views/AboutView.vue'),
},
],
},
];
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
});
export default router;
动态添加路由
使用 addrouter 动态添加路由
代码示例 --- 单独添加 --- 批量添加
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/components/Home.vue'
import About from '@/components/About.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
component: Home
}
]
})
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
// 单独添加
router.addRoute('about', {
path: '/about',
component: About
})
// 批量添加
const arrRoute = [
{ path: '/about', component: About },
{ path: '/contact', component: Contact },
];
arrRoute.forEach((route) => {
router.addRoute(route);
});
export default router
注意点
1. 报错信息:Uncaught ReferenceError: Cannot access 'router' before initialization
位置需要再 router实例化之后 new VueRouter()之后
2. 暂无
路由跳转 && 传参
编程式导航
// 使用 $router.push() 方法进行路由跳转
this.$router.push('/home');
// 使用 $router.replace() 方法进行路由跳转
this.$router.replace('/home');
// 带参数的路由跳转
this.$router.push({ path: '/user', query: { id: 1 } });
// 访问查询参数
this.$route.query.name;
// 使用命名路由和参数
this.$router.push({ name: 'UserProfile', params: { userId: 123 } });
// 接收路由参数
this.$route.params.id,
接收参数
声明式导航
<!-- 跳转到指定路径 -->
<router-link to="/path">Go to Path</router-link>
<!-- 使用命名路由 -->
<router-link :to="{ name: 'UserProfile', params: { userId: 123 }}">User Profile</router-link>
<!-- 带有查询参数 -->
<router-link :to="{ path: '/search', query: { keyword: 'vue' }}">Search Vue</router-link>
子路由
父路由通过标签 获取子路由数据
<router-view></router-view>
导航守卫
共七中
1. to: route 即将要进入的目标
1.
2. form:ruote 当前路由正要离开的路由
3. next:function 一定要调用这个方法进入下个页面 next(false) 不进行跳转取消 后续不再执行
守卫的执行先后顺序 === 进入阶段
1. beforeEach 全局前置守卫
2. beforeEnter 路由独享守卫
3. beforeRouteEenter 组件路由前置
4. beforeResolve 全局解析守卫
5. afterEach 全局后置守卫
6. 进入生命周期的流程
演示图 === 无子组件
演示图 === 有子组件
全局导航守卫
全局前置守卫
1. beforeEach((to,form,next)=>{})
2. 在进入路由前会进入beforeEach进行判断,
3. 启动beforeEach后必要填写条件 next() 才能进入路由
4. 应用:登录验证,没有登录重定向到登录页,
代码
// 全局组件守卫 == 登录验证 == 使用思路
router.beforeEach((to, form, next) => {
console.log('========我是路由前置守卫========');
if (to.path === '/login' || localStorage.getItem('token')) {
next();
} else {
next('/login');
}
});
全局解析守卫
1. router.beforeResolve((to,form,next)=>{})
2. 执行时机: 在全局导航守卫之后,但在所有路由组件和异步路由组件都被解析完毕之后、进入下一个页面之前,执行的。
3. 应用: 认证或授权操作,登录超时验证 例如检查用户是否已登录等。
代码
// 全局解析守卫
router.beforeResolve((to, from, next) => {
console.log('========我是路由后置守卫========');
// 执行一些逻辑操作 比如 认证或授权操作
next()
})
全局后置守卫
1. router.afterEach((to,from)=>{})
2. 在路由跳转后执行,没有next函数,不会影响导航本身
3. 进入next函数之后,生命周期之前
3. 应用场景: 数据埋点,记录跳转日志,跳转动画
代码
// 后置全局导航守卫
router.afterEach((to, from) => {
console.log("========我是全局后置守卫========");
// console.log(to, from);
});
路由独享守卫
路由独享守卫可以直接在路由配置中定义,只会在该路由匹配时调用
只有一个 beforeEnter
1. 写法与全局守卫不同 -- 是路由中的一个属性对象
beforeEnter: (to, from, next) => {}
2. 执行时机在全局前置守卫之后,全局解析守卫之前执行。
2. 应用:权限控制,需要特定条件才能进入的场景
代码 --- 注意用法 --- 写在路由对象里
const router = new VueRouter({
routes: [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
// 检查是否具备管理员权限
if (!isAdmin) {
next('/login'); // 如果没有权限,则跳转到登录页面
} else {
next(); // 具备权限,继续路由跳转
}
},
},
],
});
组件内守卫
beforeRouteEnter
在进入路由前被调用
1. 在全局解析守卫beforeResolve之前
2. 在全局前置守卫beforeEach和路由独享守卫beforeEach之后
beforeRouteUpdate
1. 在子组件更新时触发:比如切换子路由
2. 应用场景: 切换子路由时进行权限检查
beforeRouteLeave
1. 在退出页面时触发,可以弹出提示框,确定是否离开
2. 注意:直接关闭页面不生效
3. 应用场景:数据保存、取消导航 仅在通过路由离开页面生效关闭浏览器应用不生效:
在线时间统计(推荐使用beforeunload)
表单修饰符
目录
1. 表单修饰符
2. 事件修饰符
3. 鼠标按键修饰符
4. 键值修饰符
5. v-bind修饰符
表单修饰符
**应用与V-model**
1. lazy: <input type="text" v-model.lazy="value">
"当光标离开时,才会将值赋予给value,也就是Chagne事件之后"
2. trim: <input type="text" v-model.trim="value">
"自动过滤用户输入的空格字符"
3. number: <input v-model.number="age" type="number">
"会将用户的值转化为数值类型,如果无法被 parseFloat 解析,则会返回原来的值"
事件修饰符
1. stop: <button @click.stop="shout(1)">ok</button>
"阻止事件冒泡,相当于调用了event.stopPropagation
【stop Pro pe gei shen】"
2. prevent:<form v-on:submit.prevent="onSubmit"></form>
"阻止事件的默认行为相当于调用了event.preventDefault方法"
3. self:<div v-on:click.self="doThat">...</div>
"当前元素自身时触发处理函数,"
4. once: <button @click.once="shout(1)">ok</button>
"只会触发一次,第二次就不会触发了"
5. native : <my-component v-on:click.native="doSomething"></my-component>
"在自定义标签中默认只能监听自定义事件,监听原生事件,需要通过native"
6. passive: <div v-on:scroll.passive="onScroll">...</div>
7. capture:
"使事件触发从包含这个元素的顶层开始往下触发"
鼠标按钮修饰符
1. left 左键点击 <button @click.left="shout(1)">ok</button>
2. right 右键点击 <button @click.right="shout(1)">ok</button>
3. middle 中间点击 <button @click.middle="shot(1)">ok</button>
键盘修饰符
1. 普通键(enter、tab、delete、space、esc)
2. 系统修饰键(Ctrl、alt、shift...)
v-bind 修饰符
1. sync
能对props进行一个双向绑定
//父组件
<comp :myMessage.sync="bar"></comp>
//子组件
this.$emit('update:myMessage',params);
"子组件传递的事件名格式必须为update:value,其中value必须与子组件中props中声明的名称完全一致"
2. prop
<input id="uid" title="title1" value="1" :index.prop="index">
设置自定义标签属性,避免暴露数据,防止污染HTML结构
3. camel
将命名变为驼峰命名法,如将view-Box属性名转换为 viewBox
<svg :viewBox="viewBox"></svg>
应用场景
1. stop:阻止事件冒泡
2. native:绑定原生事件
3. once:只执行一次
4. self:将事件绑定在自身身上,相当于阻止事件冒泡
5. prevent:阻止默认事件
6. caption:用于事件捕获
7. KeyCode:监听特定按键按下
8. right:右键
Vuex
介绍
推荐大型项目使用
1. Vuex是专门为Vue打造的全局状态管理工具 --- 库
2. 推荐大型项目使用
优点: 用起来方便,
缺点: 使用不当容易造成全局数据混乱
核心模块
五个核心模块: state、getters、mutations、actions、modules
jsimport { mapState, mapMutations, mapActions, mapGetters } from 'vuex';
模块 | 对应调用方法 |
---|---|
state | ...mapState([‘xxx’]) |
getters | ...mapGetters({“xxx”:xxx,}) |
mutations | ...mapMutations({“xxx”:xxx,}) |
actions | ...mapActions({“xxx”:xxx,}) |
modules | ....... |
1. computed中调用
...mapState
...mapGetters
2. methods中调用
...mapMutations
...mapActions
示例
vuex
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
// 存储状态 类似 data
state: {
count: 0,
},
getters: {
// 同步方法 修改 state状态 计算属性 类似 computed
getCount(state) {
return state.count * 2;
},
},
mutations: {
// 同步方法 修改 state状态
setCountUp(state, value) {
state.count++;
},
setCountDown(state, value) {
state.count = state.count - 1;
},
},
actions: {
// Fetch API 进行的Get请求
getListData({ commit }) {
fetch('http://127.0.0.1:3000/')
.then((res) => res.json())
.then((res) => {
console.log(res);
});
},
// Fetch API 进行的Post请求
postListData({ commit }) {
console.log('Fetch API Post请求');
fetch('http://127.0.0.1:3000/Fetch', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 请求头
},
body: JSON.stringify({
name: '明楼',
age: 26,
}),
})
.then((res) => res.json())
.then((res) => {
console.log(res);
});
},
},
modules: {},
});
Vuex 的应用 -- 引入 -- 根据不同的 模块使用不同 的 方法
<template>
<div>
<h1>Vuex</h1>
<div>{{ count }}</div>
<div>"Getters":{{ getCount }}</div>
<button @click="setCountDown()">setCountDown</button>
<button @click="setCountUp()">setCountUp</button>
<button @click="getListData()">请求Get</button>
<button @click="postListData()">请求Post</button>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
// 方法
methods: {
// 调用Vuex中的方法 调用方法 属性在 computed
...mapMutations({
setCountDown: 'setCountDown',
setCountUp: 'setCountUp',
}),
// 调用actions中的异步方法
...mapActions({
postListData: "postListData",
getListData: "getListData",
})
},
// 计算属性
computed: {
...mapGetters(["getCount",]),
// 获取vuex中的属性状态
...mapState(["count"])
},
}
</script>
Keep-Alive
缓存组件
1. 使用方式,使用 keep-alive 标签包裹组件
2. 三个属性: include、exclude、max
3. include: 指定可以进行缓存的组件
4. exclude: 指定不需要进行缓存的组件
5. max: 指定接收最大缓存的数量
6. include、exclude接收的参数类型:String | RegExp | Array
7. nax: 接收数据类型: String | Number
include 示例 --- 接收参数类型:String | RegExp | Array
<keep-alive include="componentName"></keep-alive>
<keep-alive :include="['componentName1', 'componentName2']"</keep-alive>
<keep-alive include="componentName"></keep-alive>
<!-- 正则示例 -->
<keep-alive :include="/^Component/" :exclude="/^ComponentB$/">
<!-- 匹配以 "Component" 开头的所有组件,使用 /^ComponentB$/ 来排除 ComponentB 组件 -->
</keep-alive>
exclude 示例 --- 接收参数类型:String | RegExp | Array
<keep-alive exclude="componentName"></keep-alive>
<keep-alive :exclude="['componentName1', 'componentName2']"</keep-alive>
<!-- 正则示例 -->
<keep-alive :include="/^Component/" :exclude="/^ComponentB$/">
<!-- 匹配以 "Component" 开头的所有组件,使用 /^ComponentB$/ 来排除 ComponentB 组件 -->
</keep-alive>
max 示例 --- 接收的参数类型 Number | String
<keep-alive :max="10"></keep-alive>
实现原理
1. keep-alive包裹的组件,Vue 会将该组件实例缓存起来,不再执行销毁的生命周期钩子
2. keep-alive内部维护了一个缓存池,通常是Map或WeakMap结构,来储存缓存的组件实例,缓存在内存中不进行销毁
3. 通过LRU策略(Least Recently Used 最近最少使用原则)策略管理缓存的实例数量
示例
父组件
<template>
<div>
<router-link to="/alive">GO To alive</router-link>
<router-link to="/keep">GO To Keep</router-link>
<!-- 缓存组件 -->
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
子组件 alive
<template>
<div>
<h1>alive</h1>
<input
type="text"
v-model="msg" />
</div>
</template>
Max 的淘汰机制
1. 使用LRU算法,缓存淘汰策略(最近最少使用原则): Least Recently Used
2. 当访问数据项的时候时,数据项会被移动到链表尾部
3. 当淘汰数据时,链表尾部的数据项会被删除和替换
4. 添加时,检查缓存,没满添加,满了的话删除最近使用最少的链尾的数据项
NextTick
$nextTick 的作用
1.在下次DOM更新完毕之后,执行回调函数
2.主要应用场景
- 修改数据后获取更新后的DOM元素信息。
- 在组件更新后执行一些DOM操作或其他操作。
- 异步更新。修改数据后或在组件更新后获取最新的DOM信息,或是执行一些其他操作。
3. 利用的JavaScript事件循环的微任务宏任务会在同步任务之后执行的机制,在DOM结构渲染完完毕后的mounted阶段、数据对象更新完毕后的updated阶段最后
nextTick 的更新机制
在内部维护了一个任务队列
根据不同的环境进行降级操作
1. Promise.then
2. MutationObserver
3. setImmediate
4. setTimeout
使用场景
1. 想要在修改数据后获取到准确的更新值
插槽
介绍
1. 什么是插槽
插槽是vue中的内置组件,可以直接调用使用
2. 插槽的作用
1. 可以让我们数据传递更加灵活
2. 作用域插槽可以让父组件访问子组件作用域中的数据,实现更灵活的代码编写,
3. 多个匿名插槽,会出现多个渲染(插了3条数据,两个匿名插槽,出现6条数据)
三种插槽
1. 具名插槽
具名插槽是具有名称的插槽 可以起个名字name="xxx"
2. 匿名插槽
没有名字的插槽
3. 作用域插槽
Element-ui组件 data直接传给了组件 可以通过 template+作用域插槽,来获取数据
4. 注意点
1. 在子组件标签内的标签会被统一放进匿名插槽 自上而下。
2. 具名插槽需要<template v-slot:centent></template>标签内填写标签
3. 作用域插槽
1. <template v-slot:header="data">{{data.data}}</template>
2. <slot name="header" :data="{ name: name, age: age }"></slot>
具名插槽和匿名插槽(默认)
子组件
1. 子组件使用slot占位
<slot></slot>
<slot name='centent'></slot>
父组件
1. 在引入的子组件标签内使用template插入数据
2. 没有template的会自上而下被放进匿名插槽,也就是直接卸载子组件中的标签数据会被放进匿名插槽
<Child>
<template v-slot:centent>
</template>
<p>没有template的会自上而下被放进匿名插槽</p>
</Child>
效果图
作用域插槽
子组件
<!-- 子组件 -->
<template>
<div>
<h2>作用域插槽</h2>
<div>
<h1>标题:<slot name="title">我是默认标题1</slot></h1>
</div>
<slot
name="header"
:data="{ name: name, age: age }"></slot>
</div>
</template>
<script>
export default {
data() {
return {
name: '明楼',
age: 26,
};
},
};
</script>
父组件
<!-- 父组件.vue -->
<template>
<div>
<h2>父组件</h2>
<!-- 作用域插槽 -->
<Scopet>
<template v-slot:default="data">
<div>
{{ data }}
</div>
</template>
<template v-slot:header="data">
<div>
{{ data.data }}
</div>
</template>
</Scopet>
</div>
</template>
<script>
import Scopet from '@/components/插槽/作用域插槽.vue';
export default {
name: 'ParentComponent',
components: {
Scopet,
},
};
</script>
效果图
自定义指令
1. vue2自定义指令有五个生命周期阶段
2. bind、inserted、update、componentUpdate、unbind
3. 应用场景
- 获取焦点 v-focus
- 表单验证
- 自定义指令实现手机号码格式化
生命周期
1. bind:在指令第一次绑定到元素上时生效
2. inserted:DOM渲染完成后生效,类似mounted
3. update:虚拟DOM Vnode更新时调用。可能发生在子组件还未更新时
4. ComponentUpdated:在 DOM 更新后触发,可以再这里获取更新后的DOM结构
5. unbind:指令与元素解绑时调用,可以用来进行清理工作
bind
1. 执行时间在:
beforeMount -- bind -- inserted -- mounted
2. 在指令绑定到组件到元素上时生效
inserted
1. 执行在bind之后,mounted之前
2. 此阶段在DOM渲染后生效
update
1. 此阶段类似于beforeUpdate
2. 在组件或绑定的值更新时触发
componentUpdated
1. 此阶段类似于updated
2.在DOM更新完成之后触发,可以再这里获取更新之后的DOM结构
onbind
1. 指令与元素解绑时调用onbind,可以进行清理工作,
2. 可以进行清理工作,比如清理事件监听、定时器
触发效果图
使用 v-model 修改值,使用 this.$destroy 触发销毁()
全局自定义指令
全局配置 Vue.directive()
// main.js
Vue.directive('myDirective', {
bind: (el, binding, vnode, oldVnode) => {
// 指令绑定时的逻辑
},
instered: () => {
//指令被插入到元素时调用
},
});
组件内自定义指令
vue2 可以通过选项式 API directives:{}
// 组件内
<script>
export default {
directives: {
// 生命周期
assgin: {
// 指令第一次绑定到元素时调用,可以进行初始化工作,添加事件监听,设置初始值等
bind(el, binding, vnode, oldVnode) {
console.log("我是bind");
},
// 指令被插入到元素时调用,这里元素已经被插入到父节点中了
inserted(el, binding, vnode, oldVnode) {
console.log("我是inserted");
},
// 指令所在的绑定值,发生变化时调用,通常在这里更新元素的状态
update(el, binding, vnode, oldVnode) {
console.log("我是update");
},
// 指令所在组件的虚拟DOM全部更新完成后调用,通常在这里操作$nextTice实现延迟执行.
componentUpdated(el, binding, vnode, oldVnode) {
console.log("我是componentUpdated");
},
// 指令与元素解绑时调用,可以进行清理工作,比如移除事件监听器,删除DOmain元素
unbind() {
console.log("我是unbind");
},
},
// 获取焦点
focus:{
inserted(el,binding,vnode,oldVonde){
el.focus()
}
},
// 失去焦点时触发,创建了事件监听器,全局方法,onbind阶段进行了销毁
blur: {
inserted(el, binding) {
window.blurHandler = () => {
console.log(el.value);
}
el.addEventListener("blur", window.blurHandler)
},
unbind(el, binding) {
el.removeEventListener('blur', window.blurHandler);
delete window.blurHandler
},
},
}
}
执行顺序
可能会遇到的问题
样式问题,有外边距元素不是紧贴视口
创建项目的样式问题,有外边距元素不是紧贴视口
1. 修改入口文件 App.js中的样式
body {
height: 100vh;
width: 100vw;
margin: 0px;
}
简单的过度动画
隐入隐出
<template>
<transition
name="fade"
mode="out-in">
<router-view></router-view>
</transition>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>