1、库和框架
Vue是一个渐进式(逐步集成更多的功能)的javascript框架
库:封装属性或方法
框架:拥有自己的规则和元素,比库强大的多
Vue开发模式:传统&工程化开发方式(基于webpack,推荐)
2、@vue-cli
// 安装@vue-cli
yarn global add @vue/cli
// 查看@vue-cli是否安装成功
vue -V
// 创建脚手架
vue create 项目名称
// 启动服务器
yarn serve
3、文件说明
main.js:项目的打包入口文件——Vue初始化
App.vue:Vue页面入口
index.html:浏览器运行的文件
main.js => App.vue => index.html
4、webpack配置文件vue.config.js
module.exports = {
devServer: {
port: 3000, // 修改端口号为3000
open: true, // 打包后自动打开
}
}
5、关闭eslint
// 在vue.config.js中进行配置
lintOnSave: false
6、单Vue文件
template里只能有一个根标签
js是独立作用域,变量之间互不影响
style可以使用scoped设置局部样式
7、插值表达式
目标:在dom标签中,直接插入vue数据变量
语法:{{表达式}}
vue数据变量:data函数返回的对象上的键值对
8、设计模式
设计模式:对代码分层,引入一种架构的概念
MVVM:模型(Model),视图(View),视图模型(View-Model)双向关联
好处:减少DOM操作,提高开发效率
9、指令
(1)v-bind
语法:v-bind:属性名="vue变量"
简写::属性名="vue变量"
(2)v-on:绑定事件
v-on:事件名="简单代码"
v-on:事件名="处理函数名"
v-on:事件名="处理函数(参数)"
v-on简写:v-on <=> @
处理函数:在默认导出的methods对象中定义
(3)Vue中事件处理函数中的事件对象
语法:
如果没有参数,可以直接使用e获取到事件对象
如果传了参数,想要获得事件对象,需要传入$event,处理函数接收
(4)事件修饰符
.stop -阻止事件冒泡
.prevent -组织默认行为
.once -程序运行期间,函数只执行一次
用法:@事件+事件修饰符——eg:@click.prevent
(5)按键修饰符
@keydown.enter 按下回车键
@keydown.esc 按下取消键
(6)v-model(用于表单标签)
双向数据绑定:变量变化,视图自动同步;视图变化,变量自动同步
用法:v-model="表单变量"
重点:
**下拉菜单的v-model写在select上,值为option的value
**遇到表单复选框,v-model的变量值
非数组——关联的是checked
数组——关联的是value属性
**Vue变量初始值会影响表单的默认状态
v-model修饰符:
.number:转成数值类型赋给Vue数据变量
.trim:去掉首尾的空格
.lazy:当文本框失去焦点(onchange事件发生)的时候再更新Vue数据
(7)v-text&v-html(有安全风险,XSS注入)
作用:将Vue变量设置为标签的内容
区别:v-text不会解析标签,v-html会解析标签
注意:v-html&v-text会将标签中的插值表达式覆盖
(8)v-show&v-if(赋值为true/false控制标签的显示隐藏)
v-show隐藏:采用display: none(频繁切换)
v-if隐藏:采用从DOM树上直接移除(移除)
v-if可以配合v-else或者v-else-if使用
(9)v-for
用法:
v-for="(只变量, 索引变量) in 目标结构"
v-for="值变量 in 目标结构"
目标结构可以是数组/对象/数字
目标结构变化,触发v-for的更新:
(1)数组变更方法,就会导致v-for的更新,页面更新
(2)数组非变更方法,返回一个新的数组,不会导致v-for更新,可采用覆盖数组
(3)非变更方法想让v-for更新
将非变更方法返回的新数组赋值给原来的数组
使用$set(更新的目标结构, 更新位置, 要更新的值)
10、虚拟DOM
虚拟DOM:在内存中的一个JS对象,保存DOM关键信息
好处:提高DOM更新的性能, 不频繁操作真实DOM, 在内存中找到变化部分, 再更新真实DOM(打补丁)
11、(比较新旧虚拟DOM)diff算法
(1)同级比较:根元素改变——删除重建整个DOM树
(2)同级比较:根元素不变,属性改变——DOM复用,只更新属性
(3)同级比较:子元素或内容改变
无key,就地更新
有key,按照key比较
key值要求:唯一不重复的字符串或数值(优先使用id)
key的好处:配合虚拟DOM提高更新的性能
12、动态设置class/style
// 控制是否有这个类名
:class="{类名:布尔值}"
// 控制是否有这个样式
:style="{属性名:属性值变量}"
13、过滤器(应用:日期格式化)
转换格式过滤器就是一个函数,传入值返回处理后的值,只能在插值表达式和v-bind动态属性里使用
使用场景:字符串翻转、字母转大写
// main.js中定义全局过滤器
Vue.filter('过滤器名', (值) => {return '返回处理后的值'})
// vue文件中定义局部过滤器,可以定义多个
filters: {
过滤器名字: (值) => {return '返回处理后的值'},
……
}
// 使用语法
Vue变量 | 过滤器名称
可同时使用多个过滤器,或者给过滤器传参
// 多个过滤器使用
Vue变量 | 过滤器1 |过滤器2
14、计算属性computed(购物车)
(1)一个变量的值,依赖另外一些数据计算而来的结果
computed: {
"计算属性名"() {
return "值"
}
}
(2)计算属性也是变量,所以不可以和data中的数重名
(3)计算属性的优势:
带缓存
计算属性对应的函数执行后,会把return值缓存起来
依赖项不变,多次调用都是从缓存中取之
依赖项的值发生变化时,函数会“自动”执行,并缓存新的值
(4)计算属性完整写法(给计算属性复制的时候才需要写完整写法)
computed: {
"计算属性名": {
set(值) {
},
get() {
return "值"
}
}
}
案例:
小选影响全选(every)
全选影响小选
最后进行反选
15、侦听器watch(可以侦听data/computed属性值的改变)
watch: {
变量名 (newVal, lodVal) {
// 变量名对应值改变这里自动触发
}
}
// 侦听复杂类型,或者立即执行侦听函数
watch: {
"要侦听的属性名": {
immediate: true, // 立即执行
deep: true, // 深度侦听复杂类型内变化
handler (newVal, oldVal) {
}
}
}
16、组件(可复用的页面结构、js代码)
组件优点:独立作用域、代码复用
(1)注册全局组件
// main.js中
// 1、导入组件文件
import 组件变量 from './components/组件文件'
// 2、注册组件
Vue.component('组件名称', 组件变量)
(2)注册局部组件
// .vue文件中
// 1、导入组件文件
import 组件变量 from './components/组件文件'
// 2、注册组件
components: {
组件名: 组件变量
}
(3)使用组件
<!-- 以导入注册组件MyProduct为例 -->
<!-- 格式1 -->
<MyProduct></MyProduct>
<!-- 格式2 -->
<MyProduct />
<!-- 小写,驼峰式短杠连接 -->
<my-product></my-product>
(4)组件样式不冲突scoped
(5)父给子传值(自定义属性)
在父组件中给子组件绑定自定义属性,在子组件中使用props接收自定义属性传过来的值
循环使用组件:变量和组件都是独立的
(6)单项数据流
从父到子的数据流向
子组件修改数据不通知父亲,父组件的值不会改变
props属性为只读的
(7)子向父传值(自定义函数)
在父组件内,给子组件绑定自定义事件和事件处理函数
语法:@自定义事件名="父methods里函数名"
子组件内,恰当的时机,触发父绑定的自定义事件,导致父methods里事件处理函数执行
(8)组件间的传值(EventBus)
17、Vue的生命周期(从在页面上创建到销毁的过程——Vue实例的生命周期)
(1)生命周期:创建-挂载-更新-销毁
(2)钩子函数
初始化:beforeCreate created
created生命周期中,可以向服务器发送请求并且操作data中的数据
挂载:beforeMount mounted
mounted生命周期中,真实DOM已经挂载,可以操作页面上的DOM元素
更新:beforeUpdate updated
updated生命周期中,可以获取到修改之后的真实DOM
销毁:beforeDestroy destroyed
在这两个钩子函数中,可以停掉已经弃用但是仍然在执行的定时器
18、使用axios发送网络请求
(1)导入axios
import axios from 'axios'
(2)设置请求的基地址
axios.defaults.baseUrl = '请求地址'
(3)将axios挂载到Vue实例对象上
Vue.prototype.$axios = axios
(4)使用axios正式发送请求
this.$axios({
url: ''
//默认使用get方式提交数据
method: 'get/post'
// get发送数据
params: {},
// post发送数据
data: {}
}).then(res => { // 因为使用axios请求回来的数据格式是Promise对象,因此需要使用.then获取请求到的数据
// 业务逻辑
})
19、ref获取原生DOM元素
template
<template>
<div>
<!-- 1. 获取原生DOM元素 -->
<h1 id="h" ref="myH">我是一个h1</h1>
<!-- 2. 获取组件对象 - 可调用组件内一切 -->
<Demo ref="de"></Demo>
<!-- 3. vue更新DOM是异步的 -->
<p ref="myP">{{ count }}</p>
<button @click="btn">点击count+1, 马上提取p标签内容</button>
</div>
</template>
demo
<template>
<div>
<p>我是Demo组件</p>
</div>
</template>
<script>
export default {
methods: {
fn(){
console.log("demo组件内的方法被调用了");
}
}
}
</script>
script
<script>
import Demo from './Demo'
export default {
mounted(){
// 通过id获取DOM元素
console.log(document.getElementById("h")); // h1
// 给DOM元素添加ref属性,通过this.$refs获取DOM元素
console.log(this.$refs.myH); // h1
// 使用子组件的时候,为其设置ref属性,可以通过this.$refs获取到子组件的属性和方法
let demoObj = this.$refs.de;
demoObj.fn()
},
components: {
Demo
},
data(){
return {
count: 0
}
},
methods: {
btn(){
// vue监测数据更新, 开启一个DOM更新队列(异步任务),因此直接获取不到修改后的DOM内容
this.count++;
// console.log(this.$refs.myP.innerHTML); // 0
// 原因: Vue更新DOM异步
// 解决: this.$nextTick()
// 过程: DOM更新完会挨个触发$nextTick里的函数体,$nextTick是一个异步微任务
this.$nextTick(() => {
console.log(this.$refs.myP.innerHTML); // 1
})
}
}
}
</script>
20、动态组件
<component :is="保存组件名称的变量"></component>
// 组件缓存
<keep-alive>
<component :is="保存组件名称的变量"></component>
</keep-alive>
// 组件缓存触发激活钩子函数
activated () {}
deactivated () {}
21、组件插槽
(1)匿名插槽
<!-- Child.vue -->
<div>
<slot>默认内容</slot>
</div>
<!-- Parent.vue -->
<Child>代替内容</Child>
(2)具名插槽
<!-- Child.vue -->
<div>
<slot name="title">默认内容</slot>
<slot name="content">默认内容</slot>
</div>
<!-- Parent.vue -->
<Child>
<template v-slot:title>title内容</template>
<template #content>content内容</template>
</Child>
(3)作用域插槽
<!-- Child.vue -->
<template>
<div>
<p>{{obj.title}}</p>
<slot :data="obj">默认内容</slot>
</div>
</template>
<script>
export default {
data() {
return {
obj: {
title: 'title',
content: 'content'
}
}
}
}
</script>
<!-- Parent.vue -->
<Child>
<template v-slot="scope">
<p>{{scope.data.content}}</p>
</template>
</Child>
22、自定义指令
inserted函数触发:指令所在标签,被插入到网页上触发
(1)全局注册
Vue.directive('自定义指令名', {
inserted(el) {
// 可以对el标签扩展额外功能
}
})
(2)局部注册
directives: {
自定义指令名: {
inserted(el) {
// 可以对el标签扩展额外功能
}
}
}
(3)自定义指令传值
directives: {
自定义指令名: {
inserted(el, binding) {
// binding可以接收到指令传的值
},
update(el, binding) {
// 指令对应数据/标签更新时,此方法执行
}
}
}
23、路由
(1)路由的基本使用(声明式导航)
router-link-exact-active(准确匹配)和router-link-active(模糊匹配)
URL中的hash值和to的值完全相同,就是准确匹配,会添加2个类名
URL中的hash值包含to的值时,就是模糊匹配,值天价router-link-active
// 使用路由跳转页面
<router-link to="路由"></router-link>
// 设置挂载点-路径切换时,显示对应的组件
<router-view></router-view>
// 导入路由模块
import VueRouter from 'vue-router'
// 注册路由模块
Vue.use(VueRouter)
// 设置路由的路径以及对应组件
const routes = [{
path: 路由的路径,
component: 路由对应的组件
}, {
……
}]
// 生成路由对象
const router = new VueRouter({
routes
})
new Vue({
// 将路由对象注入到vue实例中,this可以访问$route和$router
router,
render: h => h(App),
}).$mount('#app')
(2)声明式导航跳转传值
查询字符串传值
传值:to="/path?参数名=值"
接收:$route.query.属性名
动态路由传值
传值:to="/path/:属性名"
接收:$route.params.属性名
(3)路由重定向(redirect: 强制要切换到的路由)
// 将/重定向到/path
{
path: '/',
redirect: '/path'
}, {
path: '/path',
component: 组件名
}
(4)路由404设置
{
path: '*',
component: 组件名
}
(5)路由模式
hash路由:http://localhost:8080/#/home
history路由:http://localhost:8080/home(以后上线需要服务器支持,否则找的是文件夹)
修改路由模式:在实例化路由对象时,传入mode选项和值修改
const router = new VueRouter({
routes,
mode: 'history'
})
(6)编程式导航
使用name进行跳转的时候,URL的hash值还是切换path路径值
$router.push({ path: '路由路径' })
$router.push({ name: '路由名称' })
跳转传参
使用path进行传参会忽略params
传参推荐使用:name+query
如果当前url上"hash值和?参数"与将要跳转到的"hash值和参数"一致,会爆出冗余导航问题,不会跳转路由
(6)路由嵌套
一级路由从/开始定义
二级路由往后path直接写名字
嵌套路由在上级路由的children数组里编写路由信息对象
const routes = [{
path: '/',
redirect: '/index'
}, {
path: '/index',
component: Index,
children: [{
path: 'content', // 等同于/index/contents
component: Content
}]
}]
(7)路由导航守卫
语法:router.beforeEach((to, from, next) => {})