Vue是个
渐进式的JavaScript框架
渐进式:逐渐增强,可以在项目中使用vue的一部分功能,也可以使用vue的全家桶来管理整个项目。
框架:是一套完整的解决方案。框架实现了大部分的功能,我们需要按照框架的规则写代码。比如: vue react angular …
- 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
// yarn 安装依赖
yarn add less-loader@7.2.1 less -D
// npm 安装依赖
npm i less-loader@7.2.1 less -D
lang="less"开启less的功能{{属性}}
- 使用的属性必须在data中存在
- 不能使用if、for、switch等语句
可以使用简单的表达式(三元运算符等)- 不能在标签属性中使用
- 不能在单标签内使用
作用:动态读取HTML中的标签元素的属性值
语法:
- 写法一:v-bind:属性名=“属性值”
- 写法二::属性名=“属性值”
作用:注册事件
一)语法:
- 写法一:v-on:事件名=“XXX”
- v-on:事件名=“少量代码”
- v-on:事件名=“methods中的函数名”
- v-on:事件名=“methods中的函数名(参数1,……)”
- 写法二:@事件名=“XXX”
- @事件名=“少量代码”
- @事件名=“methods中的函数名”
- @事件名=“methods中的函数名(参数1,……)”
二)事件对象:
- 没有传参
直接形参取值- 传递参数
$event三) 事件修饰符(常用的):
- .prevent
- .stop
四)按键修饰符(常用的):
- .enter
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-if 连着写
用法和前面js中讲的一样
vue提供的一个双向数据绑定的语法糖;
底层:@input + :value
表单元素的使用:输入框、单选,、多选、下拉
修饰符:
- .number 将输入框的类型改为number类型
- .trim 去除首尾空格
- .lazy 将表单的input事件该外change事件
v-html --> innerHTML ————>解析标签
v-text —> innerText————>不解析标签
作用:
遍历数组:
v-for = "item in arr" v-for = "(item, index) in arr"
- 1
- 2
遍历对象
v-for = "(value, key) in obj"
- 1
遍历数字
// 可以用来遍历下拉框中是数字的情况(月份) v-for = "item in 12"
- 1
- 2
Tip:都要动态绑定key
- Vue会尽可能的就地(同层级,同位置),对比虚拟dom,复用旧dom结构,进行差异化更新。
- 就地复用的好处在于可以复用旧的dom结构,更新高效!
虚拟DOM只是一层对真实
DOM的映射,以JavaScript对象 (VNode节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上 通过虚拟DOM,
vue可以对这颗抽象树进行创建节点, 删除节点以及修改节点的操作, 经过diff算法得出一些需要修改的最小单位, 再更新视图,减少了dom操作,提高了性能
- 策略1:先同层级根元素比较
- 如果根元素变化,那么不考虑复用,整个dom树删除重建
- 如果根元素不变,对比出属性的变化更新,并考虑往下递归复用。
- 策略2:对比同级兄弟元素时,默认按照下标进行对比复用。
- 如果指定了key,就会按照相同key的元素来进行对比复用
:class="对象"
:class="{类名: true/false, ...}"
:class="数组"
:style="对象"
:style="数组"
什么时候用?
- 当一个属性的结果需要依赖其它属性计算得来, 此时, 我们就可以把这个属性定义成计算属性
- 计算属性要定义在computed选项中
- 使用注意
- 计算属性必须定义在computed选项中
- 计算属性必须是一个函数, 必须是有返回值
- 计算属性不能被当做函数调用, 要作为属性使用
它的优势
- 计算属性有缓存:
- 基于依赖项的值进行运算并缓存,
- 只要依赖项的数据不变, 其它地方使用该计算属性都是直接从缓存中读取
略势:
- 页面中有多处要使用该计算属性, 那么, 它的性能是不是很高
核心点
- 必须写在computed选项中
- 写法是一个函数
- 默认情况函数必须有返回值
- 计算属性依赖的项变化了, 会自动重新计算
- 相比于函数, 计算属性有缓存功能
状态
默认状态(简略写法):
计算属性是只读不改,如果需要修改属性值,那么必须写完整写法
完整写法(应用:复选框的全选反选)
Person: { get() {return ...}, set(value) { ....} }
- 1
- 2
- 3
- 4
'属性名'(newVal, oldValue){
.....
}
'对象名.属性名'(newVal, oldValue){
...
}
应用场景:数据存储到本地、深度侦听数组
属性名: { immediate: true, // 可选, 是否立即执行 deep: true, // 深度侦听 // handler 固定函数 handler(newValue){ .... } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
独立的, 结构/行为/样式一体, 可复用的vue实例
好处
可维护性高、可复用性高(提高开发效率)
将一个完整的页面, 拆分成一个个组件的过程 ==> 组件化
1. 创建
.vue
2. 引入
路径一定要对
.vue后缀可以省略
3. 注册
全局注册
main.js中引入
Vue.component('组件名', 组件对象)
推荐
Vue.component(大驼峰命名, 组件对象)
局部注册
组件内引入
components选项中注册
4. 使用
组件名当做标签来使用
- 大驼峰命名法
HmButton
- 短横线命名法
hm-button
产生背景:写在组件中的样式默认是全局样式, 但是, 大多数情况希望是局部样式
如何解决?
- 子组件中在props选项中定义需要接收数据的属性
- 以标签属性的形式传递数据
this.$emit('自定义事件名', 参数1, ....)
methods 选项中定义函数
- 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是一个语法糖: :value + @input
应用于表单元素
- 会根据不同的表单元素, 绑定不同的值, 监听不同的事件
- 文本框 :value + @input
- 文本框.lazy :value + @change
- 复选和单选 :checked + @change
应用于组件
- v-model
- :value 子组件props接收 value属性
- @input 子组件触发自定义事件 this.$emit(‘input’, 传递的参数)
- 场景:
- 父传子, 传单个数据
- 子传父, 更新数据
作用:
ref和$refs配合, 可以帮我们获取真实的DOM和组件
语法:
- 给目标元素/组件, 添加ref属性
- 通过this.$refs.xxx获取到DOM和组件
作用场景:
通过常规操作已经完成不了, 此时可以真实获取DOM或组件直接操作
一般在一些三方的UI组件库使用时会经常用到
写法:
this.$nextTick(()=>{ .... })
- 1
为什么要用?
- 因为vue视图是异步更新的, 如果要获取最新的dom结构, 需要等dom更新完毕, 才去获取
- 在上一个宏任务执行完毕后, 会把收集的DOM更新一次性直接更新掉
- 在下一个宏任务中去获取最新的DOM结构即可
//占位置 //is的值决定了此处展示的一个组件
- 1
- 2
使用场景非常固定:
- 类似选项卡的场景
- 占位的地方要可能会展示多个组件
Vue.directive('组件名', {
inserted(el, binding){},
update(el, binding){}
})
// 选项 directives
directives: {
指令名: {
inserted(el, binding){},
update(el, binding){}
}
}
前提:对DOM操作功能的封装
插槽的分类
//作用 :占位置
// 子组件内
中间可以写东西,如果父组件里没有写则会展示
// 父组件内
内容部分
// 简写
内容部分
- 给插槽以添加属性的方式传值
- 1
- 将添加的所有属性收集到一个对象中
{money: 10000, age: 18}
- 1
- 使用组件的是, 给插槽提交内容是就可以用=来接收
- 初始化阶段
- 运行阶段
- 销毁阶段
// 初始化阶段
beforCreate
created // 可以操作数据
beforeMount
mounted // 可以操作DOM
// 运行阶段
beforeUpdate
updated
// 销毁阶段
beforeDestroy
destroyed // 手动释放资源(定时器、延时器、服务器资源等)
vue官方提供的路由插件
组件分类
复用组件:components文件夹
页面组件:views文件夹
(vue2 版本)
- 固定配置: 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. 全局注册路由插件 (生成两个组件:) Vue.use(VueRouter) 4. 创建路由对象 const router = new VueRouter({ }) 5. 把路由对象注入Vue实例对象 (this.router / this.route) new Vue({ ... router }).$mount('#app')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 自己配置 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. 配置路由出口
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
// vue2 版本
yarn add vue-router@3.5.3
npm i vue-router@3.5.3
// 可以用于配置导航高亮
// 内置了两个样式类
// 模糊匹配和精准匹配
// 模糊匹配
router-link-active
// 精准匹配
router-link-exact-active
- 动态路由传参
1) 路由对象 {path: '/my/:id', component: 组件名} 2) 页面跳转 this.$router.push({ path: '/my/101' }) 3) 对应页面组件获取 this.$route.params.id
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- query方式传参
1) 路由对象 不用改 {path: '/my', component: 组件名} 2) 页面跳转 this.$router.push({ path: '/my?id=101' }) 3) 对应页面组件获取 this.$route.query.id
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
配置路由规则数组> ```
- params内存方式传参(了解)
1) 路由对象 {path: '/my', component: 组件名, name: 'my'} 2) 页面跳转 this.$router.push({ name: 'my', params: { car: xxx} }) 3) 对应页面组件获取 this.$route.params.car 了解就可以忘掉了 存储在内存中的, 刷新会丢失
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 声明式导航
xxx
- 1
- 编程式导航
// 函数中 this.$router.push('/path?xxx=xxx') // 模板中 $router.push('/path?xxx=xxx')
- 1
- 2
- 3
- 4
- 5
- 6
// 配置路由规则数组内第一个位置写
{path: '/', redirect: '路径'}
- 创建NotFound页面组件
- 在路由规则数组的尾部配置一个路由对象
// 配置路由规则数组内尾部写 {path: '*', component: NotFound}
- 1
- 2
// 和routes并列
hash模式
#/xxx
history模式
/xx/xx
mode: 'history'
mode: 'hash'
先确定二级页面组件属于哪一个一级页面组件
在一级路由对象中
children : []
配置的时候, 二级的路由对象的path是不用加 /
创建仓库
在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 } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
state
定义
state: {
car: '劳斯莱斯'
}
界面使用
方式一
$store.state.car
方式二
computed选项
computed: {
...mapState('car')
}
定义
getters: {
myCar(state){
return '我的' + state.car
}
}
界面使用
方式一
$store.getters.myCar
方式二
computed选项
computed: {
...mapGetters('myCar')
}
定义
mutations: {
changeCar(state){
....
}
}
界面使用
方式一
$store.commit('changeCar', 参数)
方式二
methods选项
methods: {
...mapMutations('changeCar')
}
定义
actions: {
threeChangeCar(ctx){
....
}
}
界面使用
方式一
$store.dispacth('threeChangeCar', 参数)
方式二
methods选项
methods: {
...mapActions('threeChangeCar')
}
分仓库
namespaced: true
用法同上
区别在于要加仓库名称