Vue2知识点总结
Vue的基础概念
Vue是什么
Vue是个
渐进式的JavaScript框架
什么是渐进式和框架
渐进式:逐渐增强,可以在项目中使用vue的一部分功能,也可以使用vue的全家桶来管理整个项目。
框架:是一套完整的解决方案。框架实现了大部分的功能,我们需要按照框架的规则写代码。比如: vue react angular ...
什么是MVVM框架
MVVM思想:一种软件架构模式,决定了写代码的方式。
- M:model数据模型(ajax获取到的数据)
- V:view视图(页面)
- VM:ViewModel 视图模型
MVVM通过
数据双向绑定让数据自动地双向同步 不在需要操作DOM
- V(修改视图) -> M(数据自动同步)
- M(修改数据) -> V(视图自动同步)
拓展:其他架构模式
MVC:后端的一种架构模式
- M: model模型(业务模型)
- V:view视图(用户页面)
- C:controller控制器(逻辑处理)
MVP:是由MVC演变而来的一种架构模式
- M: model模型(业务模型)
- V:view视图(用户页面)
- P:persenter控制器(逻辑处理)
脚手架的使用
全局安装脚手架/包
// npm 安装
npm i @vue/cli -g
// yarn 安装
yarn add @vue/cli global
查看版本
vue --version
vue -V
创建项目
vue create 项目名(不能使用中文,不能以数字、符号开头)
进入项目根目录
cd 目录名
运行项目
// 用哪种方式取决于创建项目时选择的是哪种安装方式
// npm 方式运行项目
npm run serve
// yarn 方式运行项目
yarn serve
style中开启less功能
- 安装依赖
// yarn 安装依赖
yarn add less-loader@7.2.1 less -D
// npm 安装依赖
npm i less-loader@7.2.1 less -D
- 在style标签内可以通过
lang="less"开启less的功能
插值表达式(小胡子语法)
语法
{{属性}}
注意点
- 使用的属性必须在data中存在
- 不能使用if、for、switch等语句
可以使用简单的表达式(三元运算符等)- 不能在标签属性中使用
- 不能在单标签内使用
指令
v-bind
作用:动态读取HTML中的标签元素的属性值
语法:
- 写法一:v-bind:属性名="属性值"
- 写法二::属性名="属性值"
v-on
作用:注册事件
一)语法:
写法一:v-on:事件名="XXX"
- v-on:事件名="少量代码"
- v-on:事件名="methods中的函数名"
- v-on:事件名="methods中的函数名(参数1,……)"
写法二:@事件名="XXX"
- @事件名="少量代码"
- @事件名="methods中的函数名"
- @事件名="methods中的函数名(参数1,……)"
二)事件对象:
- 没有传参直接形参取值
- 传递参数$event
三) 事件修饰符(常用的):
- .prevent
- .stop
四)按键修饰符(常用的):
- .enter
v-if 和 v-show
v-if
语法:v-if="布尔值"
- 底层: 创建和删除元素
- 适用:要么显示要么隐藏
tip:惰性的, 如果不展示是默认不会创建的v-show
语法:v-show="布尔值"
- 底层: 控制css的display属性
- 适用:频繁切换(tab栏)
不同点:
控制手段不同
v-show隐藏则是为该元素添加css--display:none,dom元素依旧还在v-if显示隐藏是将dom元素整个添加或删除性能消耗不同
v-if有更高的切换消耗v-show有更高的初始渲染消耗
v-else 和 v-else-if
和 v-if 连着写
用法和js中基本一样(写在标签上)
v-model
vue提供的一个双向数据绑定的语法糖;
底层:@input + :value
表单元素的使用:输入框、单选,、多选、下拉
修饰符:
- .number 将输入框的类型改为number类型
- .trim 去除首尾空格
- .lazy 将表单的input事件该外change事件
v-html 和 v-text
v-html --> innerHTML ————>解析标签
v-text ---> innerText————>不解析标签
v-for
作用:
遍历数组:
v-for = "item in arr" v-for = "(item, index) in arr" ```遍历对象
v-for = "(value, key) in obj" ```遍历数字
// 可以用来遍历下拉框中是数字的情况(月份) v-for = "item in 12" ```
Tip:都要动态绑定key
vue中高性能对比策略
就地复用策略
- Vue会尽可能的就地(同层级,同位置),对比虚拟dom,复用旧dom结构,进行差异化更新。
- 就地复用的好处在于可以复用旧的dom结构,更新高效!
虚拟DOM
虚拟DOM只是一层对真实
DOM的映射,以JavaScript对象 (VNode节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上 通过虚拟DOM,
vue可以对这颗抽象树进行创建节点, 删除节点以及修改节点的操作, 经过diff算法得出一些需要修改的最小单位, 再更新视图,减少了dom操作,提高了性能
对比策略-diff算法
策略1:先同层级根元素比较
- 如果根元素变化,那么不考虑复用,整个dom树删除重建
- 如果根元素不变,对比出属性的变化更新,并考虑往下递归复用。
策略2:对比同级兄弟元素时,默认按照下标进行对比复用。
- 如果指定了key,就会按照相同key的元素来进行对比复用
动态设置样式
动态设置样式类
:class="对象"
:class="{类名: true/false, ...}"
:class="数组"
动态设置行内样式
:style="对象"
:style="数组"
计算属性
什么时候用?
当一个属性的结果需要依赖其它属性计算得来, 此时, 我们就可以把这个属性定义成计算属性
计算属性要定义在computed选项中
使用注意
- 计算属性必须定义在computed选项中
- 计算属性必须是一个函数, 必须是有返回值
- 计算属性不能被当做函数调用, 要作为属性使用
它的优势
计算属性有缓存:
- 基于依赖项的值进行运算并缓存,
- 只要依赖项的数据不变, 其它地方使用该计算属性都是直接从缓存中读取
略势:
- 页面中有多处要使用该计算属性, 那么, 它的性能是不是很高
核心点
- 必须写在computed选项中
- 写法是一个函数
- 默认情况函数必须有返回值
- 计算属性依赖的项变化了, 会自动重新计算
- 相比于函数, 计算属性有缓存功能
状态
默认状态(简略写法):
- 计算属性是只读不改,如果需要修改属性值,那么必须写完整写法
完整写法(应用:复选框的全选反选)
Person: { get() {return ...}, set(value) { ....} }
侦听器
侦听简单类型数据
'属性名'(newVal, oldValue){
.....
}
'对象名.属性名'(newVal, oldValue){
...
}
侦听复杂数据类型(必须写完整写法)
应用场景:数据存储到本地、深度侦听数组
属性名: { immediate: true, // 可选, 是否立即执行 deep: true, // 深度侦听 // handler 固定函数 handler(newValue){ .... } }
组件化
组件
独立的, 结构/行为/样式一体, 可复用的vue实例
好处
可维护性高、可复用性高(提高开发效率)
组件化
将一个完整的页面, 拆分成一个个组件的过程 ==> 组件化
组件的基本使用
1. 创建
.vue
2. 引入
路径一定要对
.vue后缀可以省略
3. 注册
全局注册
main.js中引入
Vue.component('组件名', 组件对象)
推荐
Vue.component(大驼峰命名, 组件对象)
局部注册
组件内引入
components选项中注册
4. 使用
组件名当做标签来使用
组件的命名规范(单双标签都可以,根据项目需要)
- 大驼峰命名法HmButton
- 短横线命名法hm-button
scoped解决样式冲突
产生背景:写在组件中的样式默认是全局样式, 但是, 大多数情况希望是局部样式
如何解决?
实现原理:
给组件中的所有标签都加了一个自定义属性:v-data-hash
把组件内的所有选择器 ---> 属性选择器
组件通信
父传子
- 子组件中在props选项中定义需要接收数据的属性
- 以标签属性的形式传递数据
子传父
子组件发射自定义事件给父组件, 并且可以携带参数
this.$emit('自定义事件名', 参数1, ....)
父组件注册自定义事件, 在函数中接收传递过来的数据 并做业务
<Son @自定义事件名="函数名" />
methods 选项中定义函数
props验证
基础类型校验
- String
- Number
- Boolean
- Date
- Array
- Object
- Function
- ...
支持多个类型
[String, Number, Boolean, ...]
必传项
{
type: String,
required: true
}
默认值
// 简单数据类型
{
type: Number,
default: 10000
}
// 复杂数据类型
// 默认值是对象
{
type: Object,
default: () => {}
}
// 默认值是数组
{
type: Array,
default: ()=>[]
}
自定义校验(了解)
{
validator(value){
return true // 校验通过
return false // 校验不通过
}
}
v-model在组件中的使用
v-model是一个语法糖: :value + @input
使用场景:
应用于表单元素
会根据不同的表单元素, 绑定不同的值, 监听不同的事件
- 文本框 :value + @input
- 文本框.lazy :value + @change
- 复选和单选 :checked + @change
应用于组件
v-model
:value 子组件props接收 value属性
- @input 子组件触发自定义事件 this.$emit('input', 传递的参数)
场景:
- 父传子, 传单个数据
- 子传父, 更新数据
ref和$refs
作用:
ref和$refs配合, 可以帮我们获取真实的DOM和组件
语法:
- 给目标元素/组件, 添加ref属性
- 通过this.$refs.xxx获取到DOM和组件
作用场景:
通过常规操作已经完成不了, 此时可以真实获取DOM或组件直接操作一般在一些三方的UI组件库使用时会经常用到
$nextTick
写法:
this.$nextTick(()=>{ .... })为什么要用?
- 因为vue视图是异步更新的, 如果要获取最新的dom结构, 需要等dom更新完毕, 才去获取
- 在上一个宏任务执行完毕后, 会把收集的DOM更新一次性直接更新掉
- 在下一个宏任务中去获取最新的DOM结构即可
动态组件
<component></component> //占位置 <component :is="组件名"></component> //is的值决定了此处展示的一个组件使用场景非常固定:
- 类似选项卡的场景
- 占位的地方要可能会展示多个组件
自定义指令
自定义全局指令
Vue.directive('组件名', {
inserted(el, binding){},
update(el, binding){}
})
自定义局部指令
// 选项 directives
directives: {
指令名: {
inserted(el, binding){},
update(el, binding){}
}
}
前提:对DOM操作功能的封装
插槽
插槽的分类
默认插槽
<slot></slot> //作用 :占位置
具名插槽
// 子组件内
<slot name="名字">中间可以写东西,如果父组件里没有写则会展示</slot>
// 父组件内
<template v-slot:名字>内容部分</template>
// 简写
<template #名字>内容部分</template>
插槽传值-作用域插槽
给插槽以添加属性的方式传值
<slot name="名字" money="10000" age="18" ....></slot>将添加的所有属性收集到一个对象中
{money: 10000, age: 18}使用组件的是, 给插槽提交内容是就可以用=来接收
组件生命周期
三大阶段
- 初始化阶段
- 运行阶段
- 销毁阶段
八个钩子函数
// 初始化阶段
beforCreate
created // 可以操作数据
beforeMount
mounted // 可以操作DOM
// 运行阶段
beforeUpdate
updated
// 销毁阶段
beforeDestroy
destroyed // 手动释放资源(定时器、延时器、服务器资源等)
路由
vue-router是什么?
vue官方提供的路由插件
组件分类
复用组件:components文件夹
页面组件:views文件夹(vue2 版本)
如果使用vue-router?
- 固定配置: 5步骤
1. 下载vue-router, Vue2项目注意版本: 3.5.3 yarn add vue-router@3.5.3 2. 在main.js中去引入VueRouter插件 import VueRouter from 'vue-router' 3. 全局注册路由插件 (生成两个组件: <router-link/> <router-view />) Vue.use(VueRouter) 4. 创建路由对象 const router = new VueRouter({ }) 5. 把路由对象注入Vue实例对象 (this.router / this.route) new Vue({ ... router }).$mount('#app')
- 自己配置 2步骤
1. 配置路由规则数组 (至关重要) - 在views文件夹中去创建页面组件 - 在main.js中引入页面组件 import Find from '@/views/Find' import My from '@/views/My' import Part from '@/views/Part' - 配置路由规则数组 const router = new VueRouter({ // 3.1 配置路由规则数组 routes: [ {path: '/find', component: Find}, {path: '/my', component: My}, {path: '/part', component: Part} ] }) 2. 配置路由出口 <router-view />
安装vue-router
// vue2 版本
yarn add vue-router@3.5.3
npm i vue-router@3.5.3
router-link 组件
// 可以用于配置导航高亮
// 内置了两个样式类
// 模糊匹配和精准匹配
// 模糊匹配
router-link-active
// 精准匹配
router-link-exact-active
路由页面传参
动态路由传参
1) 路由对象 {path: '/my/:id', component: 组件名} 2) 页面跳转 this.$router.push({ path: '/my/111' }) 3) 对应页面组件获取 this.$route.params.id query方式传参
1) 路由对象 不用改 {path: '/my', component: 组件名} 2) 页面跳转 this.$router.push({ path: '/my?id=101' }) 3) 对应页面组件获取 this.$route.query.id 配置路由规则数组> ```
params内存方式传参(了解即可)
1) 路由对象 {path: '/my', component: 组件名, name: 'my'} 2) 页面跳转 this.$router.push({ name: 'my', params: { car: xxx} }) 3) 对应页面组件获取 this.$route.params.car 了解就可以忘掉了 存储在内存中的, 刷新会丢失
两种跳转方式
- 声明式导航
<route-link to="/path?xxx=xxx"> xxx</route-link>
- 编程式导航
// 函数中 this.$router.push('/path?xxx=xxx') // 模板中 $router.push('/path?xxx=xxx')
路由重定向
// 配置路由规则数组内第一个位置写
{path: '/', redirect: '路径'}
页面404
- 创建NotFound页面组件
- 在路由规则数组的尾部配置一个路由对象
// 配置路由规则数组内尾部写 {path: '*', component: NotFound}
路由模式
// 和routes并列
hash模式
#/xxx
history模式
/xx/xx
mode: 'history'
mode: 'hash'
路由嵌套
先确定二级页面组件属于哪一个一级页面组件
在一级路由对象中
children : []
配置的时候, 二级的路由对象的path是不用加 /
vuex
如何使用vuex
创建仓库
在state队形中定义状态
界面使用
$store.state.xxx // 借助辅助函数 import { mapState } from 'vuex' computed: { ...mapState(['count']) } // 辅助函数底层实现 mapState(['count', 'car']) // 等价于 { count () { return this.$store.state.count }, car () { return this.$store.state.car } }
vuex核心语法
state
state
定义
state: {
car: '劳斯莱斯'
}
界面使用
方式一
$store.state.car
方式二
computed选项
computed: {
...mapState('car')
}
getters
定义
getters: {
myCar(state){
return '我的' + state.car
}
}
界面使用
方式一
$store.getters.myCar
方式二
computed选项
computed: {
...mapGetters('myCar')
}
mutations
定义
mutations: {
changeCar(state){
....
}
}
界面使用
方式一
$store.commit('changeCar', 参数)
方式二
methods选项
methods: {
...mapMutations('changeCar')
}
actions
定义
actions: {
threeChangeCar(ctx){
....
}
}
界面使用
方式一
$store.dispacth('threeChangeCar', 参数)
方式二
methods选项
methods: {
...mapActions('threeChangeCar')
}
modules
分仓库
namespaced: true
用法同上
区别在于要加仓库名称