一、Vue快速上手
1、Vue概念
vue 是一个用于构建用户界面的渐进式框架,由数据驱动
vue 的两种使用方式
- vue 核心包开发:局部模块改造
- vue 核心包与 vue 插件 工程化开发:整站 开发
2、 创建实例
1、准备容器
<div id="app"></div>
2、导包
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
3、创建Vue实例
const vue = new Vue()
4、指定配置项 => 渲染数据
const vue = new Vue({
// el:配置选择器
el: '#app',
// data:提供数据
data: {
msg: 'hello vue',
h: 'VUE!'
}
})
<div id="app">
<h1>{
{ h }}</h1>
{
{ msg }}
</div>
3、插值表达式
插值表达式语法:{ { 表达式 }}
利用表达式插值,渲染到页面
<p>{
{ title }}</p>
4、响应式特性
数据的响应式处理:数据变化,视图自动更新。数据驱动视图更新
- 访问数据: "实例.属性名"
- 修改数据: "实例.属性名"= "新值"
二、Vue指令
1、常用指令
指令(Directives)是 Vue 提供的带有 v- 前缀 的 特殊 标签属性。
- 内容渲染指令(v-html、v-text)
- 条件渲染指令(v-show、v-if、v-else、v-else-if)
- 事件绑定指令(v-on)
- 属性绑定指令 (v-bind)
- 双向绑定指令(v-model)
- 列表渲染指令(v-for)
2、内容渲染指令
v-text
(类似innerText):将 uame 值渲染到 p 标签中,覆盖 p 标签原有内容
<p v-text="uname">hello</p>
v-html
(类似 innerHTML):将 intro 值渲染到 p 标签中,能够将HTML标签的样式呈现出来。
<p v-html="intro">hello</p>
3、条件渲染指令
条件判断指令,用来辅助开发者按需控制 DOM 的显示与隐藏。
v-show:(控制元素显示隐藏)切换 display:none 控制显示隐藏。
v-if:(条件渲染)基于条件判断,是否创建 或 移除元素节点。
语法:v-show = "表达式",v-if = "表达式"。表达式值为 true 显示, false 隐藏。
v-else 和 v-else-if:
- 作用:辅助v-if进行判断渲染
- 语法:v-else v-else-if="表达式"
- 需要紧接着v-if使用
4、事件绑定指令
<button v-on:事件名="内联语句">按钮</button>
<button v-on:事件名="处理函数">按钮</button>
<button v-on:事件名="处理函数(实参)">按钮</button>
v-on
: 简写为 @
(1)内联语句:
<button @click="cnt++">+</button>
<div>{
{ cnt }}</div>
<button @click="cnt--">+</button>
(2)处理函数:
事件处理函数写在methods中,methods中的函数内部的this都指向Vue实例
<div id="app">
<button @click="hd">切换</button>
<h3 v-show="hide">method函数切换显示和隐藏</h3>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
hide: true
},
methods: {
hd() {
this.hide = !this.hide
}
}
})
</script>
(3)调用传参:
如果不传递任何参数,则方法无需加小括号;methods方法中可以直接使用 e 当做事件对象
如果传递了参数,则实参
$event
表示事件对象(固定用法)
<div id="app">
<div class="box">
<h3>小黑自动售货机</h3>
<button @click="fn(5)">可乐5元</button>
</div>
<p>银行卡余额:{
{ money }}元</p>
</div>
const app = new Vue({
el: '#app',
data: {
money: 100
},
methods: {
fn(i) {
this.money -= i
}
}
})
5、属性绑定指令
动态设置html的标签属性 比如:src、url、title
语法:v-bind:属性名=“表达式”,v-bind:可以简写成 :
<img :src="img">
6、列表渲染指令
v-for 列表渲染指令,用来基于一个数组来循环渲染一个列表结构。
<p v-for='(item, index) in arr'> </p>
v-for 的语法:
- item 是数组中的每一项
- index 是每一项的索引(可以省略)
- arr 是被遍历的数组
<ul>
<li v-for="(item, index) in list">{
{ index + '--' + item}}</li>
</ul>
v-for中的key:给列表项添加的唯一标识。便于Vue进行列表项的正确排序复用
Vue 的默认行为会尝试原地修改元素(就地复用)
<ul>
<li v-for="(item, index) in booksList" :key="item.id">
<span>{
{ item.name }}</span>
<span>{
{ item.author }}</span>
<button @click="del(item.id)">删除</button>
</li>
</ul>
7、双向绑定指令
1、数据变化 => 视图自动更新
2、视图变化 => 数据自动更新
语法:v-model="变量"
姓名:<input type="text" v-model="username">
v-model绑定其他表单元素:
- 输入框 input:text => value
- 文本域 textarea => value
- 复选框 input:checkbox => checked
- 单选框 input:radio => checked
- 下拉菜单 select => value
8、指令修饰符
所谓指令修饰符就是通过.
指明一些指令后缀不同的后缀封装了不同的处理操作 (简化代码)
- @keyup.enter => 当点击enter键的时候才触发
- v-model.trim => 去除首位空格
- v-model.number => 转数字
- @事件名.stop => 阻止冒泡
- @事件名.prevent => 阻止默认行为
- @事件名.stop.prevent => 可以连用 即阻止事件冒泡也阻止默认行为
9、v-bind操作class
(1)数组
<div class="box" :class="[ 类名1, 类名2, 类名3 ]"></div>
(2)对象
当class动态绑定的是对象时,键就是类名,值就是布尔值,如果值是true,就有这个类,否则没有这个类
<div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值 }"></div>
10、v-bind操作style
<div class="box" :style="{
CSS属性名1: CSS属性值, CSS属性名2: CSS属性值 }"></div>
三、computed计算属性
1、计算属性概述
基于现有的数据,计算出来的新属性。 依赖的数据变化,自动重新计算。
- 声明在 computed 配置项中,一个计算属性对应一个函数
- 使用起来和普通属性一样使用
{ { 计算属性名 }}
2、计算属性与方法区别
- computed计算属性:封装了一段对于数据的处理,求得一个结果。
- methods计算属性:给Vue实例提供一个方法,调用以处理业务逻辑。
计算属性的优势:
缓存特性(提升性能):计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算并再次缓存
methods没有缓存特性
3、计算属性的完整写法
计算属性默认的简写,只能读取访问,不能 "修改"
四、watch侦听器
1、watch侦听器
监视数据变化,执行一些业务逻辑或异步操作
watch同样声明在跟data同级的配置项中
- 简单写法:简单类型数据直接监视
- 完整写法:添加额外配置项
data: {
words: '苹果',
obj: {
words: '苹果'
}
},
watch: {
// 该方法会在数据变化时,触发执行
数据属性名 (newValue, oldValue) {
一些业务逻辑 或 异步操作。
},
'对象.属性名' (newValue, oldValue) {
一些业务逻辑 或 异步操作。
}
}
2、侦听器完整语法
- deep:true 对复杂类型进行深度监听
- immdiate:true 初始化 立刻执行一次
data: {
obj: {
words: '苹果',
lang: 'italy'
},
},
watch: {
// watch 完整写法
对象: {
deep: true, // 深度监视
immdiate:true,//立即执行handler函数
handler (newValue) {
console.log(newValue)
}
}
}
五、生命周期
1、Vue生命周期
生命周期四个阶段:① 创建 ② 挂载 ③ 更新 ④ 销毁
- 创建阶段:创建响应式数据
- 挂载阶段:渲染模板
- 更新阶段:修改数据,更新视图
- 销毁阶段:销毁Vue实例
2、Vue生命周期钩子
Vue生命周期过程中,会自动运行一些函数,被称为【生命周期钩子】→ 让开发者可以在【特定阶段】运行自己的代码
六、工程化开发
1、工程化开发和脚手架
- 核心包传统开发模式:基于html / css / js 文件,直接引入核心包,开发 Vue。
- 工程化开发模式:基于构建工具(例如:webpack)的环境中开发Vue。
工程化开发模式优点:提高编码效率,比如使用JS新语法、Less/Sass、Typescript等通过webpack都可以编译成浏览器识别的ES3/ES5/CSS等
脚手架Vue CLI: Vue CLI 是Vue官方提供的一个全局命令工具
## 1、全局安装(只需安装一次即可)
vue create project-nameyarn global add @vue/cli
## npm i @vue/cli -g
## 2、查看vue/cli版本
vue --version
## 3、创建项目架子
vue create project-name
## 4、启动项目
yarn serve
## npm run serve
2、项目目录与运行流程
- main.js 入口文件
- App.vue App根组件
- index.html 模板文件
3、组件化开发
组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。
好处:便于维护,利于复用 → 提升开发效率。
组件分类:普通组件、根组件。
Vue的三部分:
- template:结构 (有且只能一个根元素)
- script: js逻辑
- style: 样式 (可支持less,需要装包)
<template>
<div class="App">
<div class="box" @click="fn"></div>
</div>
</template>
<script>
// 导出的是当前组件的配置项
// 里面可以提供 data(特殊) methods computed watch 生命周期八大钩子
export default {
created () {
console.log('我是created')
},
methods: {
fn () {
alert('你好')
}
}
}
</script>
<style lang="less">
/*
让style支持less
1. 给style加上 lang="less"
2. 安装依赖包 less less-loader
yarn add less less-loader -D (开发依赖)
*/
.App {
width: 400px;
height: 400px;
background-color: pink;
.box {只能在注册的组件内使用
width: 100px;
height: 100px;
background-color: skyblue;
}
}
</style>
4、普通组件的注册使用
1)局部注册
特点:只能在注册的组件内使用
- 创建.vue文件(三个组成部分)
- 在使用的组件内先导入再注册
- 当成html标签使用即可 <组件名></组件名>
组件名规范:必须大驼峰命名法
// 导入需要注册的组件
import 组件对象 from '.vue文件路径'
import HmHeader from './components/HmHeader'
export default {
// 局部注册
components: {
'组件名': 组件对象,
HmHeader:HmHeaer,
HmHeader
}
}
2)全局注册
特点:全局注册的组件,在项目的任何组件中都能使用
- 创建.vue组件(三个组成部分)
- main.js中进行全局注册
- 当成HTML标签直接使用
// 导入需要全局注册的组件
import HmButton from './components/HmButton'
Vue.component('HmButton', HmButton)
5、scoped解决样式冲突
- 全局样式: 默认组件中的样式会作用到全局,任何一个组件中都会受到此样式的影响
- 局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件
<template>
<div class="base">
Base
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
scoped原理:
- 当前组件内标签都被添加data-v-hash值 的属性
- css选择器都被添加 [data-v-hash值] 的属性选择器
6、data是一个函数
一个组件的 data 选项必须是一个函数。
目的:保证每个组件实例,维护独立的一份数据对象。每次创建新的组件实例,都会新执行一次data 函数,得到一个新对象。
<script>
export default {
data: function () {
return {
count: 100,
}
},
}
</script>
七、组件通信
1、组件通信
1)组件之间如何通信
2)通信解决方案
2、父子通信
1)父子通信
- 父组件通过 props 将数据传递给子组件
- 子组件利用 $emit 通知父组件修改更新
父传子:
- 给子组件以添加属性的方式传值
- 子组件内部通过props接收
- 模板中直接使用 props接收的值
子传父:子组件利用 $emit 通知父组件,进行修改更新
- $emit触发事件,给父组件发送消息通知
- 父组件监听$emit触发的事件
- 提供处理函数,在函数的性参中获取传过来的参数
2)props概述
Props:组件上 注册的一些 自定义属性
作用:向子组件传递数据
- 可以 传递 任意数量 的prop
- 可以 传递 任意类型 的prop
3)props校验
为组件的 prop 指定验证要求,不符合要求,控制台就会有错误提示。帮助开发者,快速发现错误。
类型校验:
props: {
校验的属性名: 类型
},
完整写法:
props: {
校验的属性名: {
type: 类型, // Number String Boolean ...
required: true, // 是否必填
default: 默认值, // 默认值
validator (value) {
// 自定义校验逻辑
return 是否通过校验
}
}
},
5)props&data、单向数据流
- data 的数据是自己的 → 随便改
- prop 的数据是外部的 → 不能直接改,要遵循 单向数据流
单向数据流:父级 props 的数据更新,会向下流动,影响子组件。这个数据流动是单向的
原则:谁的数据谁负责
3、非父子通信
1)event bus 事件总线
1、创建一个都能访问的事件总线 (空Vue实例)
import Vue from 'vue'
const Bus = new Vue()
export default Bus
2、A组件(接受方),监听Bus的 $on事件
created () {
Bus.$on('sendMsg', (msg) => {
this.msg = msg
})
}
3、B组件(发送方),触发Bus的$emit事件
Bus.$emit('sendMsg', '这是一个消息')
2)provide&inject
作用:跨层级共享数据
1、父组件 provide 提供数据
export default {
provide () {
return {
// 普通类型【非响应式】
color: this.color,
// 复杂类型【响应式】
userInfo: this.userInfo,
}
}
}
2、子、孙组件 inject 获取数据
export default {
inject: ['color','userInfo'],
created () {
console.log(this.color, this.userInfo)
}
}
- provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
- 子/孙组件通过inject获取的数据,不能在自身组件内修改
八、进阶语法
1、v-model进阶
1)v-model原理
v-model本质上是一个语法糖。例如应用在输入框上,就是value属性 和 input事件 的合写
<input v-model="msg" type="text">
<input :value="msg" @input="msg = $event.target.value" type="text">
- 数据变,视图跟着变 :value
- 视图变,数据跟着变 @input
$event 用于在模板中,获取事件的形参
2)表单类组件封装
v-model其实就是 :value
和@input
事件的简写
- 子组件:props通过value接收数据,事件触发 input
- 父组件:v-model直接绑定数据
子组件
<select :value="value" @change="handleChange">
<option value="1">yx1</option>
<option value="2">yx2</option>
</select>
<script>
export default {
props: ['value'],
methods: {
handleChange(e) {
this.$emit('input', e.target.value)
}
}
}
</script>
父组件
<VmolelTest v-model="select"></VmolelTest>
2、.sync修饰符
.sync:可以实现 子组件 与 父组件数据 的 双向绑定
本质:.sync修饰符 就是 :属性名 和 @update:属性名 合写
父组件
//.sync写法
<BaseDialog :visible.sync="isShow" />
--------------------------------------
// 完整写法
<BaseDialog
:visible="isShow"
@update:visible="isShow = $event"
/>
子组件
props: {
visible: Boolean
},
this.$emit('update:visible', false)
3、ref和$refs
利用ref
和 $refs
可以用于 获取 dom 元素 或 组件实例
1、给要获取的盒子添加ref属性
<div ref="yx">我真是伊煊</div>
<button @click="change">?</button>
2、通过 $refs
获取 this.\$refs.chartRef
获取
export default {
methods: {
change() {
this.$refs.yx.innerText = '其实我是扑流萤!'
}
}
}
只用document.querySelect('.box') 获取的是整个页面中的盒子
4、异步更新 与 $nextTick
Vue 是异步更新DOM (提升性能)
$nextTick
:等 DOM更新后,才会触发执行此方法里的函数体
语法: this.$nextTick(函数体)
this.$nextTick(() => {
this.$refs.inp.focus()
})
$nextTick 内的函数体 一定是箭头函数,这样才能让函数内部的this指向Vue实例
九、自定义指令
1、基本语法
自定义指令:封装一些DOM操作,扩展额外的功能
全局注册
//在main.js中
Vue.directive('指令名', {
"inserted" (el) {
// 可以对 el 标签,扩展额外功能
el.focus()
}
})
局部注册
//在Vue组件的配置项中
directives: {
"指令名": {
inserted (el) {
// 可以对 el 标签,扩展额外功能
el.focus()
}
}
}
在使用指令的时候,一定要先注册,再使用,否则会报错
使用指令语法: v-指令名。
<input type="text" v-focus/>
注册指令时不用加v-前缀,但使用时一定要加v-前缀
2、自定义指令传参
在绑定指令时,可以通过“等号”的形式为指令 绑定 具体的参数值
<div v-color="color">我是内容</div>
通过 binding.value
可以拿到指令值,指令值修改会 触发 update 函数
directives: {
color: {
inserted (el, binding) {
el.style.color = binding.value
},
update (el, binding) {
el.style.color = binding.value
}
}
}
十、插槽
1、默认插槽
让组件内部的一些 结构 支持 自定义
- 组件内需要定制的结构部分,改用
<slot></slot>
占位 - 使用组件时,
<MyDialog></MyDialog>
标签内部, 传入结构替换slot - 给插槽传入内容时,可以传入纯文本、html标签、组件
2、后备内容(默认值)
封装组件时,可以为预留的 <slot>
插槽提供后备内容(默认内容)。
- 外部使用组件时,不传东西,则slot会显示后备内容
- 外部使用组件时,传东西了,则slot整体会被换掉
<template>
<div class="dialog-test">
<slot>我是默认内容</slot>
</div>
</template>
<template>
<div id="app">
<DialogTest>我是自定义内容</DialogTest>
<DialogTest></DialogTest>
</div>
</template>
3、具名插槽
一个组件内有多处结构,需要外部传入标签,进行定制
- 多个slot使用name属性区分名字
- template配合v-slot:名字来分发对应标签
v-slot的简写: v-slot —> #
<template>
<div class="dialog-test2">
<div>name:
<slot name="name"></slot>
</div>
</div>
</template>
<DialogTest2>
<template #name>伊煊</template>
</DialogTest2>
4、作用域插槽
定义slot 插槽的同时, 是可以传值的。给 插槽 上可以 绑定数据,将来 使用组件时可以用
1、给 slot 标签, 以 添加属性的方式传值
<slot :id="item.id" msg="测试文本"></slot>
2、所有添加的属性, 都会被收集到一个对象中
{
id: 3, msg: '测试文本' }
3、在template中, 通过 #插槽名= "obj"
接收,默认插槽名为 default
<MyTable :list="list">
<template #default="obj">
<button @click="del(obj.id)">删除</button>
</template>
</MyTable>
十一、路由
1、单页应用程序介绍
单页应用程序SPA:是指所有的功能都在一个html页面上实现
网易云音乐 https://music.163.com/
- 单页应用类网站:系统类网站 / 内部网站 / 文档类网站 / 移动端站点
- 多页应用类网站:公司官网 / 电商类网站
优点:页面按需更新
Vue中的路由:路径和组件的映射关系
2、路由的基本使用
路由作用:修改地址栏路径时,切换显示匹配的组件
VueRouter的使用(5+2)
固定5个固定的步骤
1、下载 VueRouter 模块到当前工程,版本3.6.5
yarn add vue-router@3.6.5
2、main.js中引入VueRouter
import VueRouter from 'vue-router'
3、安装注册
Vue.use(VueRouter)
4、创建路由对象
const router = new VueRouter()
5、注入,将路由对象注入到new Vue实例中,建立关联
new Vue({
render: h => h(App),
router:router
}).$mount('#app')
两个核心步骤
1、创建需要的组件(views目录),配置路由规则
2、配置导航,配置路由出口(路径匹配的组件显示的位置)
<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/friend">朋友</a>
</div>
<div class="top">
<router-view></router-view>
</div>
3、组件的存放目录
.vue文件 本质无区别,分类开来的目的就是为了 更易维护
src/views文件夹:页面组件 - 页面展示 - 配合路由用
src/components文件夹:复用组件 - 展示数据 - 常用于复用
4、路由的封装抽离
拆分模块,利于维护
脚手架环境下 @指代src目录,可以用于快速引入组件
5、声明式导航
1)导航链接
vue-router 提供了一个全局组件 router-link (取代 a 标签)
- 能跳转,配置 to 属性指定路径(必须) 。本质还是 a 标签 ,to 无需 #
- 能高亮,默认就会提供高亮类名,可以直接设置高亮样式
<div>
<div class="footer_wrap">
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/friend">朋友</router-link>
</div>
<div class="top">
<!-- 路由出口 → 匹配的组件所展示的位置 -->
<router-view></router-view>
</div>
</div>
实现高亮:
当前点击的链接默认加了两个class的值 router-link-exact-active
和router-link-active
- router-link-active:模糊匹配
- router-link-exact-active:精确匹配
自定义类名:
用于定制默认的两个类名
const router = new VueRouter({
routes: [...],
linkActiveClass: "类名1",
linkExactActiveClass: "类名2"
})
2)查询参数传参
传递参数:
<router-link to="/path?参数名=值"></router-link>
接受参数:
this.$route.query.参数名
3)动态路由传参
动态路由后面的参数可以随便起名,但要有语义
配置动态路由:
const router = new VueRouter({
routes: [
...,
{
path: '/search/:words',
component: Search
}
]
})
配置导航链接:
<router-link to="/path/参数值"></router-link>
对应页面组件接受参数:
this.$route.params.参数名
params后面的参数名要和动态路由配置的参数保持一致
4)动态路由参数的可选符
/search/:words 表示,必须要传参数。如果不传参数,也希望匹配,可以加个可选符?
const router = new VueRouter({
routes: [
...,
{
path: '/search/:words?', component: Search }
]
})
8、Vue路由
1)重定向
网页打开时, url 默认是 / 路径,未匹配到组件时,会出现空白
重定向 → 匹配 / 后, 强制跳转 /home 路径
{
path: 匹配路径, redirect: 重定向到的路径 },
// 比如:
{
path:'/' ,redirect:'/home' }
2)404
当路径找不到匹配时,给个提示页面。一般都配置在其他路由规则的最后面。
import NotFind from '@/views/NotFind'
const router = new VueRouter({
routes: [
...
{
path: '*', component: NotFind } // 最后一个
]
})
3)路由模式设置
路由的路径看起来不自然, 有个#
- hash路由(默认) 例如: http://localhost:8080/#/home
- history路由(常用) 例如: http://localhost:8080/home
以后上线需要服务器端支持,开发环境webpack给规避掉了history模式的问题
const router = new VueRouter({
mode:'history', //默认是hash
routes:[]
})
9、编程式导航
1)path路径跳转
不传参:
// 简单写法
this.$router.push('路由路径')
// 完整写法
this.$router.push({
path: '路由路径'
})
传参:
// 1、query传参
// 简单写法
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2')
// 完整写法
this.$router.push({
path: '/路径',
query: {
参数名1: '参数值1',
参数名2: '参数值2'
}
})
// 2、动态路由传参
// 简单写法
this.$router.push('/路径/参数值')
// 完整写法
this.$router.push({
path: '/路径/参数值'
})
2)name命名路由
适合 path 路径长的场景
不传参:
路由规则,必须配置name配置项
{
name: '路由名', path: '/path/xxx', component: XXX },
通过name来进行跳转
this.$router.push({
name: '路由名'
})
传参:
// 1、query传参
this.$router.push({
name: '路由名字',
query: {
参数名1: '参数值1',
参数名2: '参数值2'
}
})
// 2、动态路由传参
this.$router.push({
name: '路由名字',
params: {
参数名: '参数值',
}
})
10、二级路由
const router = new VueRouter({
routes: [
{
path: '/',
component: Layout,
children:[
//children中的配置项 跟一级路由中的配置项一模一样
{
path:'xxxx', component:xxxx.vue },
{
path:'xxxx', component:xxxx.vue },
]
}
]
})
这些二级路由对应的组件渲染到哪个一级路由下,children就配置到哪个路由下边
配置了嵌套路由,一定配置对应的路由出口,否则不会渲染出对应的组件
11、组件缓存
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件中。
<template>
<div class="h5-wrapper">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
keep-alive的三个属性:
- include:组件名数组,只有匹配的组件会被缓存
- exclude:组件名数组,任何匹配的组件都不会被缓存
- max:最多可以缓存多少组件实例
keep-alive的两个生命周期钩子:
- activated 当组件被激活(使用)的时候触发 → 进入这个页面的时候触发
- deactivated 当组件不被使用的时候触发 → 离开这个页面的时候触发
十二、代码规范
1、VueCli 自定义创建项目
1、安装脚手架
npm i @vue/cli -g
2、创建项目
vue create demo
3、选自定义
4、选择配置
5、选择vue的版本
6、选择是否使用history模式
7、选择css预处理
8、选择eslint的风格
9、选择校验的时机
10、选择配置文件的生成方式
11、不保存预设
2、ESlint代码规范及手动修复
ESLint:是一个代码检查工具,用来检查你的代码是否符合指定的规则(你和你的团队可以自行约定一套规则)。在创建项目时,我们使用的是 JavaScript Standard Style 代码风格的规则。
根据错误提示来一项一项手动修正。
如果你不认识命令行中的语法报错是什么意思,你可以根据错误代码去 ESLint 规则列表中查找其具体含义。
打开 ESLint 规则表,使用页面搜索(Ctrl + F)这个代码,查找对该规则的一个释义。
3、通过eslint插件来实现自动修正
- eslint会自动高亮错误显示
- 通过配置,eslint会自动帮助我们修复错误
十三、Vuex
1、Vuex 概述
Vuex 是一个 Vue 的 状态管理工具,状态就是数据。
Vuex 是一个插件,可以帮我们管理 Vue 通用的数据 (多组件共享的数据)
优点:
- 共同维护一份数据,数据集中化管理
- 响应式变化
- 操作简洁 (vuex提供了一些辅助函数)
2、Vuex的使用
1、安装vuex
yarn add vuex@3
## npm i vuex@3
2、新建 store/index.js
专门存放 vuex
为了维护项目目录的整洁,在src目录下新建一个store目录其下放置一个index.js文件。
3、创建仓库 store/index.js
// 导入 vue
import Vue from 'vue'
// 导入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 进行插件的安装初始化
Vue.use(Vuex)
// 创建仓库 store
const store = new Vuex.Store()
// 导出仓库
export default store
4、在 main.js 中导入挂载到 Vue 实例上
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
3、state 状态
State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储。
打开项目中的store.js文件,在state对象中可以添加我们要共享的数据。
// 创建仓库 store
const store = new Vuex.Store({
// state 状态, 即数据, 类似于vue组件中的data,
// 区别:
// 1.data 是组件自己的数据,
// 2.state 中的数据整个vue项目的组件都能访问到
state: {
count: 101
}
})
访问Vuex中的数据
1、模板中使用
<h1>state的数据 - {
{ $store.state.count }}</h1>
2、组件逻辑中使用
this.$store.state.xxx
3、js文件中使用
//main.js
import store from "@/store"
console.log(store.state.count)
4、提供计算属性
<h1>state的数据 - {
{ count }}</h1>
// 把state中数据,定义在组件内的计算属性中
computed: {
count () {
return this.$store.state.count
}
}
4、辅助函数mapState
mapState是辅助函数,帮助我们把store中的数据映射到 组件的计算属性中, 它属于一种方便的用法
1、导入mapState (mapState是vuex中的一个函数)
import {
mapState } from 'vuex'
2、采用数组形式引入state属性
mapState(['count'])
上面代码的最终得到的是 类似于
count () { return this.$store.state.count }
3、利用展开运算符将导出的状态映射给计算属性
computed: {
...mapState(['count'])
}
<div> state的数据:{
{ count }}</div>
5、严格模式和Vuex的单项数据流
明确 vuex 同样遵循单向数据流,组件中不能直接修改仓库的数据
通过 strict: true
可以开启严格模式,开启严格模式后,直接修改state中的值会报错。
state数据的修改只能通过mutations,并且mutations必须是同步的
6、mutations
1)mutations
mutations是一个对象,对象中存放修改state的方法。mutations必须是同步的。
1、定义mutations
const store = new Vuex.Store({
// 定义mutations
mutations: {
}
})
2、组件中提交 mutations
this.$store.commit('addCount')
2)带参数的 mutations
1、定义mutations(带参数)
const store = new Vuex.Store({
addCount (state, count) {
state.count = count
}
})
2、组件中提交 mutations
this.$store.commit('addCount', 10)
提交的参数只能是一个,如果有多个参数要传,可以传递一个对象
this.$store.commit('addCount', {
count: 10
})
3)mapMutations
mapMutations类似于mapState,把位于mutations中的方法提取了出来
import {
mapMutations } from 'vuex'
methods: {
...mapMutations(['addCount'])
}
7、actions
1)actions
state是存放数据的,mutations是同步更新数据 (便于监测数据的变化,更新视图等,方便于调试工具查看变化)
actions则负责进行异步操作
1、
mutations: {
changeCount (state, newCount) {
state.count = newCount
}
}
actions: {
setAsyncCount (context, num) {
// 一秒后, 给一个数, 去修改 num
setTimeout(() => {
context.commit('changeCount', num)
}, 1000)
}
}
2、组件中通过dispatch调用
setAsyncCount () {
this.$store.dispatch('setAsyncCount', 666)
}
2)mapActions
mapActions 是把位于 actions中的方法提取了出来,映射到组件methods中
import {
mapActions } from 'vuex'
methods: {
...mapActions(['changeCountAction'])
}
8、getters
1)getters
除了state之外,有时我们还需要从state中筛选出符合条件的一些数据(类似于computed计算属性),这些数据是依赖state的,此时会用到getters
1、定义getters
getters: {
// getters函数的第一个参数是 state
// 必须要有返回值
filterList: state => state.list.filter(item => item > 5)
}
2、使用getters
<div>{
{ $store.getters.filterList }}</div>
2)mapGetters
computed: {
...mapGetters(['filterList'])
}
<div>{
{ filterList }}</div>
9、module模块化
1)module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
如果把所有的状态都放在state中,当项目变得越来越大的时候,Vuex会变得越来越难以维护
1、准备user模块 modules/user.js
const state = {
userInfo: {
name: 'zs',
age: 18
}
}
const mutations = {
}
const actions = {
}
const getters = {
}
export default {
state,
mutations,
actions,
getters
}
2、在store/index.js
文件中的modules配置项中,注册模块
import user from './modules/user'
const store = new Vuex.Store({
modules:{
user
}
})
数据的访问:
this.$store.state.模块名.xxx
2)state模块化
尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的 state 中,属性名就是模块名
- 直接通过模块名访问
$store.state.模块名.xxx
- 通过 mapState 映射:
- 默认根级别的映射
mapState([ 'xxx' ])
- 子模块的映射 :
mapState('模块名', ['xxx'])
(需要开启命名空间namespaced:true)
- 默认根级别的映射
export default {
namespaced: true,
...
}
3)getters模块化
- 直接通过模块名访问
$store.getters['模块名/xxx ']
- 通过 mapGetters 映射
- 默认根级别的映射
mapGetters([ 'xxx' ])
- 子模块的映射
mapGetters('模块名', ['xxx'])
(需要开启命名空间)
- 默认根级别的映射
4)mutations模块化
- 直接通过 store 调用
$store.commit('模块名/xxx ', 额外参数)
- 通过 mapMutations 映射
- 默认根级别的映射
mapMutations([ 'xxx' ])
- 子模块的映射
mapMutations('模块名', ['xxx'])
(需要开启命名空间)
- 默认根级别的映射
5)actions模块化
- 直接通过 store 调用
$store.dispatch('模块名/xxx ', 额外参数)
- 通过 mapActions 映射
- 默认根级别的映射
mapActions([ 'xxx' ])
- 子模块的映射
mapActions('模块名', ['xxx'])
(需要开启命名空间)
- 默认根级别的映射
10、补:json-server
json-server用于模拟后端接口服务环境
1、安装全局工具 json-server (全局工具仅需要安装一次)
yarn global add json-server
npm i json-server -g
2、代码根目录新建一个 db 目录
3、将资料 index.json 移入 db 目录
4、进入 db 目录,执行命令,启动后端接口服务 (使用--watch 参数 可以实时监听 json 文件的修改)
json-server --watch index.json
十四、打包优化
1、publicPath
默认情况下,需要放到服务器根目录打开,如果希望双击运行,需要配置 publicPath 配成相对路径
module.exports = {
// 设置获取.js,.css文件时,是以相对地址为基准的。
// https://cli.vuejs.org/zh/config/#publicpath
publicPath: './'
}
2、路由懒加载
当打包构建应用时,Javascript 包会变得非常大影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
const ProDetail = () => import('@/views/prodetail')
const Pay = () => import('@/views/pay')
const MyOrder = () => import('@/views/myorder')