目录
5 vue构造函数methods属性中方法内部的this指向 15
18.5 计算属性计算数组中的对象数据的某个属性对应的属性值之和 25
1.2.2 用在ref用在使用组件的桥梁上【父组件内可以通过桥梁找到子组件】 44
5.2 在组件的style标签上写个lang=’less’ 49
12.3 为什么把路由的代码放到router/index.js里 57
13.2 添加路由【当默认创建项目时是不包含路由的,需要手动添加路由】 57
13.6 准备路由规则【views/index.js】 58
13.7 实例化路由对象并使用【views/index.js】 58
13.8 暴露路由实例化对象【views/index.js】 59
13.9 入口文件导入Vue和主文件和路由index.js文件【src/main.js】 59
13.10 入口文件将路由实例对象挂载起来【src/main.js】 59
40.1 下载Postcss-px-to-viewport插件 75
40.2 提供Postcss-px-to-viewport插件的配置文件 75
43.2 在需要使用tabbar的组件内部粘贴官网的html结构 77
44.1 模块化的导入语法:commonjs【require】和es6【import】的导入语法 78
46.2 创建主题颜色文件src/styles/themStyle.less文件写主题颜色样式 81
47.1 将NavBar导航栏引入到vant.js文件里面 81
49.3 没有全局导入Toast组件时【组件内部或js文件里用】 82
51.2 将v-for动态创建的元素或组件嵌套在list的基本结构中 83
4 storage模块封装【封装在utils文件夹下面】 91
13.4 初始化vuex数据【@/store/index.js】 96
13.7 辅助函数 -mapState【简化计算属性访问vuex中的数据】 97
13.8 修改vuex中的数据【vuex中的mutations】 98
14.2 辅助函数映射子模块的说明【开启命名空间后】 105
15.2 store/index.js中导入和注册子模块 108
1.3 安装babel-plugin-import 插件 116
1.4 在项目根目录【与package.json平级】配置babel.config.js文件】 116
1.9 在工具包utils文件夹中新建4个.js文件 118
1.11 在router/index.js文件中导入需要的路由文件并创建路由规则。 119
1.13 组件中需要vant哪个部分,就在官方文档中按需导入 119
1.13.2 在路由组件中导入vant官网中的组件结构代码和js代码。 120
1.14 在需要使用utils文件夹类功能模块文件的组件中导入对应的功能模块文件,并导出需要使用的变量 120
1.17 准备后端接口json-server工具【如果需要测试接口】 120
1.18 配置相对路径【如果就想使用绝对路径可以不配置】 120
2.6 在工具包utils文件夹中新建4个.js文件 123
2.7 新建src/utils/element.js文件 124
2.8 将src/utils/element.js文件在main.js中执行一下 124
2.10 新建src/utils/request.js【封装有基地址的axios实例】 124
2.11 新建src/utils/storage.js文件【用于保存本地token】 124
3 sass/scss【以前叫sass现在叫scss】 126
第一章
1 vue基本使用
语法 | 描述 |
new Vue({ //绑定需要控制的元素 el:”选择器”, data(){ return { 变量1名:变量1值, 变量2名:变量3值, … } }, computed:{ 计算属性名1(){ return data中相关数据的表达式 }, 计算属性名2(){ return data中相关数据的表达式 } }, methods:{ 方法1名(){ //方法1体 }, 方法2名(){ //方法2体 } } }) | el后面为需要控制的元素选择器 data函数return后面为一个对象,对象里面存着我们插值表达式【{{变量名}}】需要插入的变量名。【data中的变量也叫普通属性】 computed计算属性,用法和data中的变量使用用法一致【{{计算属性名}}】,但计算属性的本质是函数,函数内部要使用data中的数据,还是需要使用this.变量名的方式,data中相关数据的表达式的返回值会给到我们的计算属性名上面,所以我们可以直接使用插值表达式来插入计算属性,用法和data中变量的用法一致。其他计算属性也可以相互使用这个返回值,用法也和data中的变量一致,this.计算属性名的方式在js代码中使用。{{计算属性名}}的方式在html结构内容中使用 methods配置后面为一个对象,对象中存放着我们需要使用的方法列表,方法体需要使用data中的变量,通过this.变量名的方式使用【操作数据后,视图会跟着数据变化,数据驱动视图,所以我们不用关注视图层。】 |
2 插值语法
语法:{{数据}}【插值表达式需要写在html中被控制的元素内部的任意子元素内部】【插值表达式数据来源:可以是计算属性变量,也可以是data配置函数中的变量,还可以是我们数组对象等相关方法等。】
作用:可以把数据渲染到页面上
别称:也叫胡子语法
插值语法里可以些任意的表达式,但是不可以写语句
表达式:例如算术表达式、逻辑表达式、三元表达式、函数表达式等等
语句:分支语句、循环语句等等
语法 | 描述 |
{{数据变量}} | 数据变量是vue实例对象的data方法的返回值中的对象中的属性名,最终返回结果 |
{{表达式}} | 表达式可以包含数据变量、操作数的任意排列组合。最终将表达式的结果渲染到页面上。 |
3.指令
Vue提供的具备某种功能的特殊的html行内属性【指令是:html行内属性,它具备特殊功能,vue提供的】
4 v-on:
v-on:的简写@
4.1 语法
语法 | 描述 |
v-on:事件类型=”要执行的少量js代码” | 只有一句赋值表达式时可以使用 |
v-on:事件类型=”methods中的函数名” | 当有多行语句,可以使用函数封装起来,如果函数不需要传递参数使用该语法 |
v-on:事件类型=”methods中的函数名(实参)” | 当有多行语句,可以使用函数封装起来,如果函数需要传递参数使用该语法 |
简化语法 | 描述 |
@事件类型=”要执行的少量js代码” | v-on:的简写为@,这里为简写 |
@事件类型=”methods中的函数名” | |
@事件类型=”methods中的函数名(实参)” |
4.2 事件类型
事件类型 | 描述 |
click | 点击事件 |
keyup | 键盘抬起事件 |
keydown | 键盘按下事件 |
5 vue构造函数methods属性中方法内部的this指向
this指向当前的vue实例对象本身
methods配置属性内部的方法体需要使用vue实例对象的变量,需要使用this.变量名的方式使用。
6 浏览器
7 vue里的事件对象
在绑定事件的时候,如果绑定事件后面的函数名加了小括号,则methods内部方法的形参为实参匹配值,没有,形参会为undefined【如果需要拿到事件对象,需要在实参中使用$event代表事件对象】。如果绑定事件后面的函数名没有加小括号,methods内部方法的形参为事件对象。
Vue的开发里很少用到事件对象,原因:vue里面如果要阻止默认行为、阻止冒泡,会有更好的方式来做,那就是事件修饰符。
语法 | 描述 |
<标签名 @事件类型=”目标函数”> | 目标函数中的形参在方法体内为事件对象 |
<标签名 @事件类型=”目标函数(参数列表,$event)”> | 目标函数$event实参对应的方法形参在方法体内为事件对象 |
7.1 事件修饰符使用语法
语法 | 描述 |
<标签名 @事件类型.事件修饰符= ”目标函数”> | 目标函数中的形参在方法体内为事件对象 |
<标签名 @事件类型.事件修饰符= ”目标函数(参数列表)”> | 目标函数$event实参对应的方法形参在方法体内为事件对象 |
7.2 事件修饰符
修饰符 | 描述 |
.prevent | 阻止默认行为【a标签的默认跳转,button默认提交跳转】 |
.stop | 阻止冒泡【点击自己只会触发自己的事件,父元素事件不会触发】 |
.once | 事件只会绑定一次【事件触发完后就解绑了】 |
.self | 事件只能由自身触发,无法由冒泡触发【点击子元素的事件自己的事件不会被触发,但子元素的事件会被触发】 |
7.3 按键修饰符
原生以前使用e.keyCode获取的ascall码确定哪个键【已废弃】;e.key获取的键盘名确定哪个键【已废弃】;现在原生使用e.code获取的key键名的方式确定按下的哪个键
键盘事件类型有keyup【键盘弹起事件】、keydown【键盘按下事件】
修饰符 | 描述 |
.enter | enter回车触发 |
.键盘的键名 | 键盘对应键触发 |
.space | 空格触发 |
.esc | 按esc触发 |
.键盘的键名1.键盘的键名2 | 只要是对应键名都可以触发 |
8 绑定属性 v-bind:
v-bind:简写::
作用:让某个行内属性不写死,简单来说就是设置动态行内属性【data中数据的改变,可以修改标签属性名对应的属性值】【有v-bind的标签属性,这个时候该属性对应的属性值为data中的变量名,会将data中的变量名对应的变量值赋值给该属性。】
8.1 语法
语法 | 描述 |
<标签名 v-bind:标签属性名=’数据’></标签名> | 这里的数据为data配置方法中的变量名,会将该变量名对应的变量值赋值给该标签属性名的属性值中。 |
简写语法 | 描述 |
<标签名 :标签属性名=’数据’></标签名> | 这里的数据为data配置方法中的变量名,会将该变量名对应的变量值赋值给该属性。 |
9 双向数据绑定 v-model
它目前是用在表单元素上,用来获取表单元素的值,也可以用来设置表单元素的值。
表单元素:放在form里面的元三年,例如input、textarea、select等
语法 | 描述 |
<表单元素 v-model=’数据’/> | 数据为data方法配置中的变量,这样我们修改该表单元素的value值时,数据的修改会更新到data对应的变量值的修改。data中对应变量的修改也会更新表单元素value值 |
当这个表单元素的值变量,就会自动把值给数据,同样数据变化了,也会设置给这个表单元素的值。
9.1 v-model用在下拉框
如果用在下拉框中,获取的时下拉框选中的value值【即option选项的value属性值】
也可以设置,如果设置了一个值,就找到value值 和 这个值一样的选项被选中
10 MVVM
10.1 什么叫设计模式
就是很多程序员总结下来的一套更高效的编程思路、编程方法,这就叫设计模式
简单来说:就是前人总结的一套更好的写代码的方式,这就叫设计模式
10.2 MVVM设计模式
10.2.1 M:
Model层,也就是数据层【数据】
10.2.2 V:
View层:也就是视图层【界面】
10.2.3 VM:
ViewModel:视图模型层【Vue提供的核心】
它内部会自动绑定数据到界面,所以数据一旦改变界面就会跟着变
它内部会自动监听界面的变化,然后把界面的值赋值给数据。
11 v-if与v-show
作用:都是控制某个元素的显示与隐藏
11.1 v-if
通过操作dom树来控制标签的显示与隐藏
当布尔值为true就显示这个标签,当布尔值为false就隐藏这个标签
语法 | 描述 |
<标签名 v-if=’布尔值’>内容</标签名> | 布尔值true创建该元素,布尔值为false销毁该元素 布尔值位置可以是表达式,也可以是字面量,还可以是变量,他们会将计算的结果进行布尔转换。 |
<标签名 v-if=’data中的变量名’>内容</标签名> | data配置函数中的变量名,变量的值为true或其他非false数据显示【创建】该标签;变量值为false,隐藏【销毁】该标签 |
11.1.2 多分支
从上往下依次判断,满足哪个条件就只渲染这个条件对应的标签,其他都不渲染
如果要双分支多分支,那肯定用v-if的组合
但是v-if是通过操作dom的创建和销毁来控制的,而v-show是通过display来控制的,所以v-show的性能更高,如果频繁切换 那建议用v-show
如果依次决定有或者没有某个标签就用v-if【比如用户登陆是否为vip】
语法 | 描述 |
<标签1 v-if=’条件1’>内容</标签1> <标签2 v-else-if=’条件2’>内容</标签2> <标签3 v-else -if=’条件3’>内容</标签3> … <标签4 v-else=’条件4’>内容</标签3> | 默认条件中的字符为data中的变量,选择第一个满足条件的元素并创建该标签元素渲染到页面上。【其他的分支元素不会被创建】,都不满足条件则将最后一个标签元素创建并渲染到页面上。 |
11.2 v-show
通过行内样式的display:none的方式显示和隐藏元素
语法 | 描述 |
<标签名 v-show=’布尔值’> | 布尔值true显示该元素,布尔值为false隐藏该元素 |
<标签名 v-show=’data中的变量名’> | data配置函数中的变量名,变量的值为true或其他非false数据显示该标签;变量值为false,隐藏该标签 |
11.3 v-if和v-show的区别
原理不一样;是否懒加载不一样
v-if | v-show |
通过操作dom树的添加和移除来控制显示和隐藏【也就会不断的创建和销魂元素】 | 通过操作行内样式的display属性来控制标签的显示和隐藏 |
v-if是惰性的,或者说是懒加载。意思是如果默认为false,压根不会渲染它,只要当为true才会去渲染 | v-show不管是true还是false,它默认都会在dom树上【也就是默认就会创建】 |
12 v-text和v-html
12.1 v-text
语法 | 描述 |
<标签名 v-text=’data中变量名’>内容</标签名> | 将标签的内容替换为data中变量名保存的数据【内容写上也会无效,数据中的标签无法解析,会直接渲染所有内容包括标签到页面上。】 |
12.2 v-html
语法 | 描述 |
<标签名 v-html=’data中变量名’>内容</标签名> | 将标签的内容替换为data中变量名保存的数据【数据中的标签可以被解析,只会将标签以外的内容渲染到页面上】 |
13 v-for
v-for创建元素时需要写key属性,可以提高vue性能,避免eslint插件报不符合规范的错【因为v-for创建标签元素基本上是根据数据来创建标签元素的,所以,我们为了避免重复给标签添加key属性,需要动态绑定key属性,以数组为例:将数据中唯一的id作为key的属性值,如果没有唯一id,那就给数组下标。】
13.1 v-for遍历数组
作用:遍历这个数组,数组有多少个元素,就产生多少个这样的标签
数组一旦删元素和加元素都会自动触发界面更新
数组通过下标修改元素不会触发界面更新【vue2的缺陷】【解决方案:将数组名[索引]=修改值改为this.$set(数组,下标,要修改的值)】【vue3把$set删掉了,因为vue3没有这个问题,可以直接通过下标修改】
完整语法 | 描述 |
<标签名 v-for=’(数据项变量,索引变量) in 目标数组’>内容</标签名> | 数据项变量【拿到数组的每一项】一般写item、value、v 索引变量一般写:index、idx、i |
语法 | 描述 |
<标签名 v-for=’数据项变量 in 目标数组’>内容</标签名> |
13.2 v-for遍历对象
对象有多少个属性就会产生多少个这样的标签,对象属性值一般使用变量名value、val、v表示,对象属性名一般使用变量名key、key表示
对象.属性名=’值’:如果这个属性名是已经存在的属性,那么就会触发界面更新【视图和界面】;如果这个属性名是动态添加的属性【也就是之前没有这个属性】,那么不会触发界面更新【只会更改数据,视图不会更新】【解决对象视图不会更新的方法:将添加新属性时的对象.属性名=’值’改为this.$set(对象,’属性名’,属性值)】
v-for动态创建元素时,推荐给标签元素添加一个key属性,且value值唯一,所以一般会有:key=’唯一性变量’这个属性【唯一性变量值只能是数值或字符串类型】
需要写key属性,后面用脚手架没有该属性会报错【安装脚手架默认会安装eslint插件,会自动检测是否符合规范,不符合规范就会报错】
完整语法 | 描述 |
<标签名 v-for=’(属性值变量,属性名变量) in 目标对象’ :key=”唯一id变量”>内容</标签名> | 目标对象有多少个属性就会动态创建多少个当前标签元素 属性值变量一般使用:value、val、v 属性名变量一般使用:key、k 内容可以包含插值表达式,插值表达式里面可以有属性值变量,也可以使用data中的变量 |
常见语法 | 描述 |
<标签名 v-for=’属性值变量 in 目标对象’ :key=”唯一id变量”>内容</标签名> | 属性值变量一般使用value 目标标签元素内部的子元素的插值表达式中可以使用目标标签元素标签内定义的变量【比如v-for后面的属性值变量】,也可以使用data中定义的变量。 |
对象,修改原本存在的属性可以触发界面更新,如果动态给对象添加一个新的属性,无法触发界面更新
13.3 v-for遍历数字
语法 | 描述 |
<标签名 v-for=’num in 目标数字’>内容</标签名> | num为变量名,变量名可以更改,且变量的初始值为数字1【内容中也可以使用插值表达式插入num变量相关表达式。】 动态创建目标数字个标签元素 |
14 动态style
14.1 语法
语法 | 描述 |
<标签名 :style=”{小驼峰样式属性1:data中变量1名,小驼峰样式属性2:data中变量2名}”>内容</标签名> | 动态style中小驼峰样式属性和css样式基本一致,只不过将多个单词用-连接符连接的样式属性改为了小驼峰样式属性。属性写法固定,属性值为data中的变量【当小驼峰样式属性名和data中变量名一样时,这里可以简写一个小驼峰样式属性名】【data中变量名保存的值需要为css样式属性匹配的值】 |
15 动态class
作用:让某个标签的类没有写死,根据数据来决定有或者没有这个类
15.1 语法
当布尔值为true表示有这个类,当布尔值为false没有这个类【可以通过表达式或者变量的方式来动态控制这个类的增加与修改】
语法 | 描述 |
<标签名 :class=”{类名:布尔值}”>内容</标签名> | 布尔值位置可以是返回值为布尔值的条件表达式,也可以是变量值为布尔值的变量名,还可以是其他字面量 |
16 v-modle的修饰符
16.1 语法
当表单类型为单选或复习框【checkbox】时,v-model绑定的变量值为true表示被选中,v-model绑定的变量值为false表示未被选中
语法 | 描述 |
<表单元素标签 v-model.v-model修饰符=’data中的变量’/> | 修饰符根据业务需求设置。这里只能使用data中的变量【且该变量不能为除了字符串之外的其他基本数据类型。】 |
16.2 修饰符
修饰符 | 描述 |
.trim | 去掉表单元素输入框首尾的空格 |
.number | 将输入的内容转为数值类型【能转多少转多少,一个都转不了就是输入的值】【默认情况下,输入的内容拿到的都是字符串,如果做相加会变成拼接,所以可以用这个修饰符转成数字】 |
.lazy | 不会边输入边将数据更新【默认情况下,输入框一边输入我们值就会跟着变化】,会等我们将所有数据输入完了,表单失去焦点或者键盘按下回车键才会提交数据来更新数据和视图。 |
17 总结
17.1 克隆项目有关
语法 | 描述 |
git clone ‘克隆地址’ | 第一次克隆项目的命令 |
git pull | 后续进入克隆好的项目,在.git平级处打开git bash命令行输入git pull即可更新最新代码 |
17.2 powershell权限不够总结
步骤 | 过程 |
1 | powershell命令行输入set-executionpolicy remotesigned |
2 | 输入y |
18 计算属性【有缓存】
计算属性本质上虽然是函数,但是它的结果会被缓存起来【缓存细节:当一个计算属性第一次使用时会调用函数并把计算的结果缓存起来,后续调用都是从缓存里取的。任何一个依赖项发生改变,都会重新调用计算属性返回一个新的结果,再把新的结果缓存起来。】【缓存:依赖项不变就只会调用一次,依赖项发生改变才会重新调用。】【普通属性怎么用,计算属性就怎么用】
什么叫依赖项:就是计算属性内部用到的别的属性或data中的变量就叫依赖项。
18.1 计算属性应用场景
当某个数据依赖别的数据产生时,就要用到计算属性
18.2 语法
简写语法 | 描述 |
computed:{ 计算属性名(){ return 表达式 } } | computed计算属性需要和data、methods平级的位置使用。 在js代码中无法对简写的计算属性进行修改 表达式为其他计算属性或者普通属性计算的返回值【js中使用属性需要使用this.属性名,即return后面的表达式需要有this.属性名】 |
完整语法 | 描述 |
computed:{ 计算属性名:{ get(){ return 表达式 }, set(形参){ //形参为计算属性,这里可以修//改计算属性的值。 } } } | computed计算属性需要和data、methods平级的位置使用。 |
18.3 v-model和计算属性一起应用
复选框的全选和反选【复选框的选中状态依赖数组的每一项中的某个属性值,只有所有该值都为true,复选框才为选中状态】
语法 | 描述 |
复选框中的自定义选中属性(){ get(){ return this.依赖数据数组.every(v=>{ return v.依赖属性 == 状态依赖属性值 }) } }, set(val){ this.依赖数据数组.forEach(v=>{ //将计算属性的属性值给对应的依赖项 v=>v.依赖属性=val }) } } | 因为复选框的选中状态依赖于数组的每一项,所以可以通过计算属性来设置复选框的选中状态。【复选框需要有v-model属性,v-model属性对应的属性值就为该计算属性,这样就可以根据计算属性的取值设置好复选框的状态。】 |
18.4 扩展every和some数组方法
应用场景:找一些都需要满足或者只需一个满足的场景。
语法 | 描述 |
目标数组名.every((item,index)=>return item相关条件表达式) | 返回值为布尔值 item为目标数组的每次循环的数组项 如果所有每次循环返回值都为true则整体返回值为true,否则返回false |
语法 | 描述 |
目标数组名.some((item,index)=>return item相关条件表达式) | 返回值为布尔值 item为目标数组的每次循环的数组项 如果所有每次循环返回值都为false则整体返回值为false,否则返回true |
18.5 计算属性计算数组中的对象数据的某个属性对应的属性值之和
语法 | 描述 |
computed:{ 计算属性名(){ return this.数组名.reduce((总计变量,数组每一项变量)=>总计变量+=数组每一项变量.需要计算的对象属性,0) } } | 这种方式可以计算得到对象数组中单个属性的所有属性值之和,后续可以直接使用插值表达式插入该计算属性即可。 |
19 侦听器
作用:可以侦听vue里某个数据的变化,一旦改变就会调用你写好的回调函数【侦听响应的状态变化:data props computed,会触发侦听器函数的执行。】
语法:跟data、methods这些同级位置
不能够在侦听器的逻辑处理函数中很突然的给需要整体的数据添加一个属性,需要初始化,否则无法侦听到新加的属性。【不能监听动态属性:新增的属性】
19.1 监听简单数据类型变量语法
如果侦听的是对象,那么除非当这个对象的指向改变了才会被触发【所以侦听数据使用19.2的深度侦听】
语法 | 描述 |
watch:{ //参数1为修改后的新值 //参数2为修改前的旧值 要侦听的数据(新数据,老数据){ //逻辑代码 } } | watch配置需要和data、methods这些同级位置。 要侦听的数据为普通属性【data中的数据变量】或计算属性【computed计算属性中的方法名】。只要要侦听的数据发生改变【简单数据类型数据】,逻辑代码就会执行。 |
19.2 监听对象
19.2.1 侦听对象的单一或少量属性的变化
语法 | 描述 |
watch:{ “需要侦听的目标对象名.属性名”(){ //逻辑代码 } } | 目标对象名的该属性名对应的属性值发生改变时执行逻辑代码 第一个参数为改变后的侦听数据,第二个参数为改变前的侦听数据 |
19.2.2 侦听对象里面任意属性的变化【深度侦听】
语法 | 描述 |
watch:{ 需要侦听的目标对象名:{ //开启深度侦听 deep:true, //当对象里任意属性改变,都会触发handler这个函数 handler(){ //逻辑代码 }, //会立即执行一次 inmediate:true } | watch配置需要和data、methods这些同级位置。 要侦听的数据为普通属性【data中的数据变量】或计算属性【computed计算属性中的方法名】。只要要侦听的数据发生改变【对象数据】,逻辑代码就会执行。 侦听数据是惰性的,当我们连续触发事件,修改侦听数据的变化,侦听器里面的逻辑是不会立即执行的,会等我们没有连续触发该事件时才会执行,加了immediate属性后,打开页面就会执行一次。 |
19.3 侦听数组项为简单数据类型的数组
通过数组索引修改数组【数组项为简单数据类型的数组】,该数组无法被侦听到,界面也无法跟新;但通过数组索引修改数组【数组项为对象的数组】,该数组同样无法被侦听到,但通过修改数组对象的某个属性时是可以更新界面的【修改对象已有属性可以更新界面】
直接通过下标修改元素,是无法被侦听到的,因为这是vue2的缺陷【vue3可以】
事件使用$set(数组,下标,要修改的值)这种方式可以被侦听到
语法 | 描述 |
watch:{ 数组名(新数组,老数组){ //逻辑代码 } } | watch配置需要和data、methods这些同级位置。 要侦听的数据为普通属性【data中的数组数据变量】。只要要侦听的数据是通过push、pop、shift、unshift、splice、sort、reverse发生改变【数组数据】,逻辑代码就会执行。【能够改变数组自身的方法都是能被侦听到的】【因为vue对数组一些方法进行了重新包裹,所以我们能监听到】 |
19.4 侦听对象数组
也就是说侦听一个数组,只不过数组里每个元素都是一个对象
通过下标去修改对象里的属性,界面会有变化【修改对象已有属性,界面是会跟着改变的】,但是无法被侦听到【保存数组变量的内存地址没有发生改变】
要想对象属性改变也能被侦听到,就要开深度侦听。
19.5 大总结
要侦听对象就需要开启深度侦听
不能触发界面跟新的情况 | 不能触发默认侦听器的情况 |
通过下标把数组元素【该数组元素是简单数据类型】修改了 | 修改对象里的属性 |
给对象添加新的属性 | 修改数组的数据项 |
20 组件的概念与组件化开发的概念
20.1 组件
可以理解为组成网页的一部分,就叫组件
开发网页时,把网页每一块都当作是一个组件,把这些组件组合在一起,就构成一个网页
页面中的一个小部分,就意味着它至少要有html结构,这一块还要有样式、可能还要有交互(js)
20.2 把网页每一小部分看作一个组件的好处
方便维护
方便复用
20.3 在vue种如何表示一个组件
用一个.vue文件表示一个组件
因为组件是网页中的一个小部分,所以它必须具备三要素:html、css、js
20.3.1 组件代码构成
语法 | 描述 |
<template> <根标签 class="类名"> 其他html结构 </根标签> </template> <script> export default { name: "当前文件名", props: [父组件传递过来的变量列表], methods: { 相关事件方法, }, }; </script> <style scoped> /* 默认样式是所有文件都可以用,加完scoped后,代表这些样式只能在本文件 */ //相关css样式代码 </style> | template标签内部写我们需要的组件结构【写html的地方】 <script> //js代码和引入其他组件的地方 export default{ //相关配置项 } </script> <style scoped> //css样式代码部分 </style> |
20.4 为什么App.vue称之为主组件
因为项目一启动渲染的就是App.vue,我们运行项目看到的就是它
20.5 vscode 插件推荐vetur
作用 | 描述 |
Vscode默认是不认识.vue文件的 | 下载该插件后就不会有错误提示 |
提供少量而且又常用的代码段 | 该插件必备 |
20.6 上线项目运行过程
步骤 | 过程 |
1 | 在项目文件夹打开cmd |
2 | 命令行输入npm run 脚本服务名【npm run serve】 |
3 | 这样服务器会去找到我们的入口文件main.js【main.js实例化Vue后面调用的$mount的作用类似之前的el,代表找到某个容器交给Vue管理】 |
4 | main.js文件是自动生成好的【里面的new Vue()实例化的过程会自动的去找到我们public/index.html文件,并将找到里面为id选择器为app的div容器,交给Vue管理;最后导入App.vue组件,并把这个组件替换渲染到找到的容器】 |
项目的入口文件为main.js
20.7 项目相关文件及文件夹说明
用脚手架创建好项目后会自动配置,我们需要该代码和放资源的文件及文件夹位置如红色字体所示文件夹或文件
文件夹或文件完整路径 | 描述 |
node_modules | 第三方包文件夹-里面有项目下载的包 |
public/favicon.ico | 网站默认图标 |
public/index.html | 网站的html文件,你看到的页面,本质上是这个html文件 |
src | 源代码文件夹 |
src/main.js | 入口文件->项目运行后运行的第一个文件 |
src/assets | 静态资源文件夹,放图片、字体图标等文件夹 |
src/components | 组件文件夹,放全局组件的位置【该文件夹下面放我们App.vue组件需要用到的组件】 |
src/app.vue | 主组件->我们页面上看到的内容其实都在App.vue里写的 |
.gitignore | 配置让git忽略的文件或者文件夹 |
babel.config.js | babel这个插件的配置文件【babel可以做语法降级等功能,解决浏览器兼容性】【真正转换的是babel.js这个插件】 |
jsconfig.json | Js核心语法的配置文件 |
package.json | 项目配置文件,里面有项目名、版本号、作者等。以及快捷命令,下载的依赖包的包名和版本号 |
README.md | 项目说明文件,可以对项目做介绍 |
vue.config.js | 脚手架的配置文件 |
yarn.lock | 表示是用yarn包管理工具管理的项目 |
package.lock.json | 表示用npm包管理工具管理的项目 |
21 脚手架
脚手架的本质相当于node的第三方包
21.1 为什么要安装脚手架
脚手架可以帮我们快速搭建好一个项目的骨架,可是我代码可能会在电脑中的任何位置,所以这种包最好装成全局包 |
我们以前写less文件,浏览器不认识,但是通过webpack里的less-loader,可以在打包时把less文件打包成css文件,所以浏览器可以运行了 |
浏览器不认识vue文件,所以也可以通过webpack里的vue-loader,在打包时把vue文件打包成浏览器认识的html、css、js文件 |
但是webpack搭建项目比较麻烦,自己还要下载多个插件和loader,然后做对应的一些配置,才能够正常运行和打包 |
所以在想有没有一个东西,能实现webpack解析vue文件的功能,而且还不用我们自己做过多的配置,有,他就是vue-cli,也俗称脚手架 |
脚手架最主要的功能就是实现了webpack的功能,但还不用我们做过多的配置。 |
21.2 安装脚手架
喜欢yarn包管理工具就用yarn,不喜欢就用npm【选一次以后默认都是用你选中的行为。需要修改可以在c:\users\自己电脑用户名/.vuerc修改配置,用vscode打开后将packageManager改成npm或者yarn用谁改谁。如果是用yarn创建的项目,就建议用yarn serve运行项目】
任意位置打开命令行
语法 | 描述 |
npm i @vue/cli -g | 全局安装脚手架,命令行输入该代码,以后可以直接使用vue create 项目名创建项目 |
win7使用命令用npm i @vue/cli@4.x -g
21.3 查看脚手架是否安装成功
语法 | 描述 |
vue -V | 小黑窗输入该命令,返回版本号代表安装成功 |
21.4 更新脚手架【升级脚手架】
语法 | 描述 |
npm update @vue/cli -g | 小黑窗输入该命令,可以升级我们的脚手架 |
21.5 脚手架-创建新项目和运行项目
项目名创建时不是中文【但创建完可以改】、只能是数字加英文和一些简单的英文符号例如-,不能大写英文,不能用别的包名作为项目名,例如vue、webpack、axios等
21.5.1 创建新项目
语法 | 描述 |
vue create 项目名 | 来到一个没有vue.js的文件夹上,打开下黑窗,输入该命令 |
敲完后我们按方向键可以上下,按回车键选中。我们目前选vue2的默认配置【Use npm就表示使用npm管理项目;Use yarn就表示使用yarn管理项目】
21.5.2 运行项目
根目录【package.json平级的位置】如果有package-lock.json文件就表示是用npm创建的项目。根目录如果有yarn.lock文件,就表示是yarn创建的项目。
根据用的是哪个包管理工具,我们需要到我们的配置中改为对应的包管理配置,后续下载包也使用对应的包管理工具下载的方式下载【c:\users\自己电脑用户名/.vuerc修改配置,用vscode打开后将packageManager改成npm或者yarn用谁改谁】
语法 | 描述 |
npm run serve | 进入创建好的项目文件夹并打开小黑窗输入该命令即可运行项目【用npm创建的项目使用该命令】 |
yarn serve | 进入创建好的项目文件夹并打开小黑窗输入该命令即可运行项目【用yarn创建的项目使用该命令】 |
21.6 每个组件的template里要有根标签
根标签:包住所有内容的最外层的标签就叫根标签,一般是div
如果template里只有一个标签,你直接写这个标签,它既是根标签也是自己的内容
但凡要有多个标签,必须要有外部的根标签包住
23 eslint
写代码时具有规则和规范两个概念
eslint是 一款语法规范的检查工具,它能让你写的代码只要不规范就会报错
23.1 规则
一定要遵守,不遵守会报错,程序无法往下运行
规则 |
名字不能时关键字,只能是字母、数字、下划线组成、并且数字不能开头 |
23.2 规范
不遵守也不会报错【但建议你这样写】
规范 |
起名要有意义【见名知意】 |
要遵守驼峰命名 |
23.3 eslint规则
以后命令行开启一个服务器【npm run serve】,只要看到控制台第一行有[eslint]报错信息,就表示代码虽然没有问题,但不符合规范
规则 |
变量声明了必须使用【声明变量后但没有使用,本质上不会报错,但是如果你装了eslint就报错了->脚手架创建的项目默认装了eslint:报错原因是它觉得你写了不规范的代码】 |
v-for创建元素必须加key属性 |
24 v-for必须加key的规范
写v-for时,建议加一个:key=”唯一值”【因为加了这个能够提高vue内部复用的性能】
以后key的值有唯一值给唯一值,没唯一值给下标
Key只能接受字符串和数值类型
语法 | 描述 |
<标签名 v-for=”变量名 in 对象名” :key=’唯一值’></标签名> | 唯一值为一个变量,变量对应的变量值只能是字符串和数值类型数据。 |
25 导入与注册组件的语法
如果自己创建的组件,在标准规范里要求使用“大驼峰命名”:也就是至少两个单词,且两个单词首字母都要大写
25.1 使用局部组件步骤
第一步导入组件【当前组件导入】:import 自定义目标组件详细信息变量名 from ‘目标组件路径/目标组件名.vue’【目标组件名可以没有.vue后缀,在脚手架里会自动找.vue后缀。】【通常情况下我们这样写import 目标组件名 from ‘目标组件路径/目标组件名.vue’】
第二步注册组件【当前组件注册】:跟data平级位置(在export default的对象里面)=>
components:{自定义使用组件标签名:自定义目标组件详细信息变量名}【通常情况下,为了可以使用es6的简写,两个变量都命名为目标组件名,所以可以这样简写components:{目标组件名列表}】
第三步使用组件:在当前组件的template标签的根标签内使用<自定义使用组件标签名></自定义使用组件标签名>【需要使用小写,多个单词用-连接起来】【目前而言写单标签和双标签效果一样】【<目标组件名></目标组件名>】
步骤 | 过程 |
1 | 下载vue脚手架npm i @vue/cli -g |
2 | 在存放项目的文件夹打开小黑窗 |
3 | 在小黑窗输入vue create 项目名 |
4 | 进入项目文件夹,找到package.json文件的scripts节点 |
5 | 在项目名文件夹另开一个小黑窗 |
6 | 输入npm run serve【可以将项目跑起来,用的yarn就用yarn serve跑】 |
7 | 4,5,6没问题可以开始改我们的代码 |
8 | 将项目名文件夹拖到我们的vscode里面【vscode只开一个项目】 |
9 | 在src->components文件夹下面创建我们的组件【组件都是.vue结尾的文件】 |
10 | 在<template>标签下面写一个根标签,根标签下面写我们需要的标签结构;<style>标签内部写组件的css代码【这个css代码默认是所有的组件共用的,很容易发生样式覆盖,如果需要让该css样式只在当前文件有效,需要给style标签添加scoped属性。】;script标签内部的先导入我们需要使用的组件import 目标组件名 from “另一个需要导入的组件路径/组件名.vue” 下方的export default {}对象内部写name属性,属性值为文件名【便于检索文件】,components配置对象属性用于导入我们需要的组件【对象中的变量列表就是我们的目标组件名】 |
11 | 当前组件使用目标组件时时只需要使用<目标组件名></目标组件名>【目标组件名可以使用原组件名,也可以使用小写字母,但多个单词用-分隔】【多个单词小写且用-连接是h5的规范】【建议拆分】 |
25.2 案例
<template> <div id="app"> //1.3使用组件 <my-header></my-header> </div> </template> <script> //1.1导入组件 import MyHeader from './components/MyHeader.vue' export default { name: 'App', //1.2注册组件,这里是es6简写,属性名和变量名一致,简写属性名 components: { MyHeader, } } </script> <style scoped> /* 默认样式是所有文件都可以用,加完scoped后,代表这些样式只能在本文件 */ </style> |
25.3 注册全局组件
上面的导入和注册组件的语法,其实是一个局部组件【在哪注册就只能在哪使用】
如果一个组件可能在多个组件里都要用,就可以注册成全局组件【全局的东西都放在main.js里】
语法 | 描述 |
//main.js中导入组件 import 组件名 from ‘组件路径/组件名.vue’ //将组件注册为全局组件 Vue.component(‘使用组件名’,组件名) | main.js中写入该代码后就注册为全局组件了 以后在其他组件中就可以直接使用<使用组件名/>的方式在不同组件中导入该组件了【不必反复注册组件】 |
26 组件的name属性
浏览器 更多工具->扩展程序 拖进了Vue.js devtools后,我们浏览器打开控制台就可以查看每个vue组件中的数据。但是当组件足够多的时候,我们需要找某个组件时,就需要给每个组件加一个name属性,属性值为该组件的文件名
组件的name属性可以让我们在开发调试工具里快速找到这个组件
组件的name属性对应的属性值推荐和组件名一致【这样便于按名字查找对应的组件】【组件的name属性对应的属性值必须是多个单词用-连接起来】
26.1 语法
语法 | 描述 |
export default { name:’组件名’ } | 在export default{}中配置name属性,属性值就为组件名,以后在开发调试工具中就可以快速找到想要的组件了【建议name属性值为组件名方便查找组件】 |
27 组件样式使用scoped属性
<style>标签没有加scoped属性之前,该样式是作用于全局的,很容易造成样式污染,当加了scoped属性后,该样式只会作用于当前组件内。
28 父子组件
28.1 父子组件的概念
父组件:包含了另外一个组件的父组件【需要导入其他组件位置的标签名<目标组件名/>:也叫子组件标签】【我把这个叫 做父组件使用子组件的桥梁】
子组件:被包含的组件就叫子组件【被使用的组件叫子组件】
28.2 组件通信
组件通信是指不同组件之间传值
28.3 父子通信之 父传子
是指把父的数据传递给子组件【实现步骤:子要父给】【子数据是从父组件中的数据来的,在父组件中修改属性名对应的数据,子组件的数据也会跟着变】【数据从父元素来的原因:防止子组件数据写死,数据写在父元素里,子组件就可以更好的复用了】【子要:子组件内部的props配置里面写需要的属性名】【父给:父组件通过自定义使用组件标签也就是那个桥梁,通过属性匹配的方式,将属性值传给子组件当作子组件中的数据属性。所以在子组件的html结构中直接可以使用查找表达式插入该属性名即可得到属性名对应的数据。】
28.3.1 子组件代码:
数组方式父传子语法 | 描述 |
<template> <div class="类名"> <标签1名>内容{{ 自定义使用子组件属性1名}}</标签1名> <标签2名>内容{{自定义使用子组件属性2名}}</标签3名> … </div> </template> export default { name: "当前文件名", props:["自定义使用子组件属性1名","自定义使用子组件属性2名",…], methods: { 方法名() { //可以获取父组件属性名对应的数据 console.log(this.父组件属性名); }, }, }; <style scoped> /* 默认样式是所有文件都可以用,加完scoped后,代表这些样式只能在本文件 */ .目标类名 { //相关样式 } </style> | 将父组件中自定义使用子组件属性名对应的属性值作为普通属性传递过来给子组件【子组件html中使用父组件中的数据,需要通过自定义使用子组件那个桥梁。需要用{{自定义使用子组件属性名}};子组件js中使用父组件数据:需要使用this.自定义使用本组件标签名中的属性名的方式将父组件内部的数据传递过来,或者传递一个写死的值也可】 props配置为子组件中的配置。属性值中写所有父组件传递过来的属性名变量【可以是数组列表,也可以是对象列表。推荐使用对象列表:因为对象可以写必传属性。而且可以控制传值类型,还可以设置默认值。】 写数组形式,父组件可以传可以不传,而且没有默认值。 |
对象方式父传子语法【推荐方式】 | 描述 |
<template> <div class="类名"> <标签1名>内容{{自定义使用子组件属性1名}}</标签1名> <标签2名>内容{{自定义使用子组件属性2名}}</标签3名> … </div> </template> export default { name: "当前文件名", props: { 父组件中自定义使用子组件属性1名:{ //开启必传模式 required:true }, 父组件中自定义使用子组件属性2名:{ default:’默认值’ }, … }, methods: { cut() { console.log(this.title); }, }, }; <style scoped> /* 默认样式是所有文件都可以用,加完scoped后,代表这些样式只能在本文件 */ .目标类名 { //相关样式 } </style> | 将自定义使用本组件标签名内部的属性名对应的属性值作为普通属性传递过来给子组件【子组件html中使用父组件属性值数据需要用{{自定义使用子组件属性1名}};子组件js中使用父组件属性值数据需要使用this. 自定义使用本组件标签名中的属性名】 props配置属性值中写所有父组件传递过来的属性名变量【可以是数组列表,也可以是对象列表。推荐使用对象列表:因为对象可以】 required:true开启必传模式 必传模式的意思就是父组件必须有该父组件目标属性名【父组件使用子组件的桥梁,桥梁必须拥有子元素required为true的属性名】 props使用对象的形式开启必传模式也可以叫做校验,也称之为props的校验 当默认值为数组或对象时,必须从一个工厂函数获取 |
28.3.2 父组件代码
当使用对象方式传递值时,可以控制传递值的数据类型
代码 | 描述 |
<template> <div> <!-- 组件 --> <自定义使用子组件名 :属性1名=”数据” :属性2名=”数据” /> </div> </template> <script> // 导入组件 import 子组件名 from "子组件路径/子组件名.vue"; export default { // 注册组件 components: { 子组件名, }, methods: {
}; </script> <style></style> | 子组件props内部定义了自定义使用子组件名在这里使用时必须要有的属性名,并且通过这个属性名将数据传递给子组件使用 自定义使用子组件名也支持v-for动态创建对应的使用子组件标签。【就相当于复用多少次子组件的内容。】 |
28.3.3 子组件中props【props校验】
语法 | 描述 |
props:{ 要接收的数据名:{ //必传 required:true //限制为某种类型 type:类型 } } | 类型有 1.Array:限制为数组 2.Object:限制为对象 3.Function:限制为函数 4.Number:限制为数值 5.String:限制为字符串 6.Boolean:限制为布尔值 |
28.4 父子通信之子传父
子修改父里的数据【但无法直接改,所以是通知父自己来改】
28.4.1 子组件代码
语法 | 描述 |
<目标标签名 @事件类型=”目标方法名”>内容<目标标签名/> | 子组件的这个标签触发事件时,会在子组件中去找目标方法名 |
methods:{ 目标方法名(){ //使用子组件的自定义目标自定义事件名一定会触发,所以会直接执行父组件中对应的方法。 this.$emit(“父组件中 目标自定义事件名”,传递给父组件方法的参数列表) } } | 参数列表的数据如果为父组件传递过来的数据,此时需要使用this.自定义使用组件标签名的行内属性名, 这句代码会通知父组件中的桥梁,让他在桥梁中找到@目标自定义事件名的属性 如果桥梁【子组件标签】中@目标自定义事件名的属性值为方法名变量,就会在父组件内去执行该方法,在该方法中就可以修改父元素自己内部的数据了。 如果桥梁【子组件标签】中@目标自定义事件名的属性值为行内js,则在行内js中$event表示参数列表的第一个参数。 |
28.4.2 父组件代码
语法 | 描述 |
<目标标签名 @目标自定义事件名=”目标事件方法名” /> | 子组件只要通知了,桥梁就会去该目标自定义事件名,父组件就会在methods配置中去执行目标事件方法名对应的方法。 |
methods:{ 目标事件方法名(子组件传递过来的参数列表){ //逻辑代码 } } |
28.4.3 代码案例
子组件代码 |
<template> <div class="product"> <h3>标题:{{ title }}</h3> <p>价格:{{ price }}</p> <p>描述: {{ desc }}</p> <button @click="cut">点击</button> </div> </template> <script> export default { name: "MyHeader", props: { title: { required: true, }, price: { required: true, }, desc: { //设置没有描述的描述默认值 default: "好好吃", }, }, methods: { cut() { console.log(this.title); this.$emit("changePrice", 2, this.title); }, }, }; </script> <style scoped> /* 默认样式是所有文件都可以用,加完scoped后,代表这些样式只能在本文件 */ .product { width: 400px; border: 3px solid #000; border-radius: 5px; margin: 10px; padding: 0 20px; } </style> |
父组件代码 |
<template> <div id="app"> <my-header v-for="k in list" :key="k.id" :title="k.title" :desc="k.desc" :price="k.price" @changePrice="chang" ></my-header> </div> </template> <script> import MyHeader from "./components/MyHeader.vue"; export default { name: "App", components: { MyHeader, }, data() { return { a: null, list: [ { id: 1, title: "隆江猪脚饭", price: 20, desc: "隆江" }, { id: 2, title: "鱼生", price: 30, desc: "四川" }, { id: 3, title: "椰子鸡", price: 25, desc: "重庆" }, { id: 4, title: "火锅", price: 100, desc: "重庆" }, { id: 5, title: "烤肉", price: 80, desc: "广东" }, { id: 6, title: "果条牛肉汤", price: 35, desc: "湖南" }, ], }; }, methods: { chang(a, b) { //a,b就是子组件$emit("changePrice",2,this.title)后面两个参数 console.log(a, b); }, }, }; </script> <style></style> |
29 单向数据流
父的数据可以流入到子【父组件中自定义使用子组件标签名的行内属性会作为子组件的data数据变量,子组件需要在export default{}中的props内部写上父组件中的自定义使用子组件标签名的行内所有属性名;子组件html中可以通过{{自定义使用子组件标签名的行内属性名}}的方式渲染父组件中的属性值数据,子组件的方法中也可以使用this.自定义使用子组件标签名的行内属性名的方式获取数据,但是不能进行修改数据。】,但子不可以反过来修改props属性列表传递过来的数据【子元素里面只能通过this.的方式获取props中从父组件传递过来的数据,不能在子组件的方法里面修改,否则会报错,会违反单向数据流的规则。】【单向数据流的本质是不能修改栈上的数据:子元素不能修改传递过来的简单数据类型的数据,但可以修改复杂数据类型的数据,也就是说,如果父传子时传递的是复杂数据类型,那么子里面改堆上的数据不报错,而且父也会跟着变】
换句话说就是:子里不能修改props的数据
30 v-model
v-model是个语法糖,他会帮我们判断表单数据类型,并帮我们生成30.1 中原理中的代码。【语法糖:写少量代码,运行时会自动帮我们生成大量代码。】
30.1 v-model原理
行内js不需要用this关键字来使用数据,直接使用即可,但在methods配置的方法内,使用数据需要使用this.数据名的方式
需要使用事件对象,在行内js中为$event,在方法中为如果没有传实参调用,事件对象为方法中第一个形参,如果传递了实参,那就需要将$event作为实参传递过来,进行参数匹配使用事件对象。
当v-model绑定的是多选框或单选框时,绑定的数据为true则勾选状态,否则取消勾选状态。
类型 | 描述 |
输入框 | v-model用在input输入框,生成的是:value=’数据变量’ 以及@input=’数据变量=$event.target.value’ |
v-model用在input输入框时,也是可以监听change事件的,只不过要加修饰符.lazy。相当于它的原理监听的就不是input事件了,而是change事件,因为对于change事件而言,就是要按回车或者失去焦点才代表输入完了 | |
下拉框 | v-model用在下拉框,生成的是:value=”数据” 以及@change=“数据=$event.target.value” |
多选框 | v-model用在多选框,生成的是:checked=“数据变量” 以及@change=”数据变量=$event.target.checked” |
30.2 $event的完整理解
当$event在元素的行内js中使用时表示事件对象,当$event在使用组件标签的那个桥梁上行内js使用时,表示子组件的this.$emit(“自定义事件类型”,参数1,…)这句代码中的参数1。
第二章
1. ref与$refs
1.1 作用
Vue里面专门用来找到dom元素和子组件对象【自定义使用那个组件对象】【以后在vue里推荐如果要找dom元素,不再用document.querySelector,而是推荐ref和$refs配合的形式找到dom元素】
1.2 语法
ref和refs用在同一个组件内【ref用在html标签结构的属性上,refs用在methods配置下的方法中用于找到ref属性对应属性值的元素。】
用ref找到子组件对象后,也可以快速的调用子组件里的方法【快速实现父传子】
1.2.1 用在子组件按钮标签上获取元素
template标签结构 | 描述 |
<按钮标签名 @事件类型=’事件处理函数名’>内容</按钮标签名> | 该按钮的事件触发时,执行该组件内methods配置内的事件处理函数名的方法 |
<目标标签 ref=’自定义目标元素名’ >内容</目标标签> | 给标签添加ref属性,以后在方法中需要找到该元素时可以通过this.$refs.自定义目标元素名的方式找到该元素 |
methods:{ 事件处理函数名(){ this.$refs.自定义目标元素名 } } | this.$refs.自定义目标元素名这句代码可以找到当前组件内具有ref属性且属性值为自定义目标元素名的的元素,可以给该元素做其他逻辑操作。 |
1.2.2 用在ref用在使用组件的桥梁上【父组件内可以通过桥梁找到子组件】【可以实现快速父传子】
<子组件 ref=”自定义子组件对象名” />
语法 | 描述 |
this.$refs.自定义子组件对象名.子组件中的属性=修改后的值。 | 会找到ref标记的组件,并直接可以获取子组件中的属性和方法 |
2. $nextTick
Vue有一个重要的特点:Vue中的dom更新都是异步的,也就是说,一定要等当前的同步代码执行完了,才会去更新dom【所以有打印dom元素里面的数据时会是以前的数据,但打印dom元素对象,会是最新数据加标签。】
$nextTick就是一个回调函数,没有参数时返回值为一个promise对象,会等下一次dom更新后调用;有参数时返回值为undefined,回调函数会等下一次dom更新后调用。
应用场景:对动态创建的dom元素进行操作。
语法1 | 描述 |
this.$nextTick(()=>{ //这里的代码一定是下次dom更新后调用的 this.$refs.refs对应的ref绑定的元素名相关逻辑 }) | 回调函数写法,也推荐,页面dom更新完后执行回调函数。且返回值为undefined。 |
语法2 | 描述 |
async function (){ await this.$nextTick() this.$refs.refs对应的ref绑定的元素名相关逻辑 } | 异步函数写法,推荐写法await this.$nextTick()表示等页面更新完毕再执行后面的代码。 |
语法3 | 描述 |
this.$nextTick() .then(()=>{ this.$refs.refs对应的ref绑定的元素名相关逻辑 }) | Promise写法,不推荐 |
3 自定义指令
3.1 自定义指令完整写法【局部】
语法 | 描述 |
directives: { 指令名:{ //当指令绑定的元素插入到dom时调用 //第一个参数el表示指令绑定的元素对象 //第二个参数表示指令绑定元素的渲染时相关信息 inserted(el,binding){ //逻辑代码 }, //第一个参数el表示指令绑定的元素 //第二个参数表示指令绑定元素更新后的相关信息 update(el,binding){ //逻辑代码 } } } | directives指令需要和data这些配置平级 最重要的就是第二个参数的value属性,获取的是绑定数据的值,在inserted方法中表示原来的数据,在update方法中表示更新后的数据。 |
简写语法 | 描述 |
directives: { //第一个参数el表示指令绑定的元素 //第二个参数表示指令绑定元素更新后的相关信息 指令名(el,binding){ //逻辑代码 } } | 当inserted方法和update方法逻辑代码一致时,可以这样简写。 |
3.2 自定义指令全局写法
语法1 | 描述 |
Vue.directive(“指令名”,{ //当指令绑定的元素插入到dom时调用 //第一个参数el表示指令绑定的元素对象 //第二个参数表示指令绑定元素的渲染时相关信息 inserted(el,binding){ //逻辑代码 }, //第一个参数el表示指令绑定的元素 //第二个参数表示指令绑定元素更新后的相关信息 update(el,binding){ //逻辑代码 } }) | 全局写在main.js中 |
语法2【简写】 | 描述 |
Vue.directive(“指令名”, (el,binding) =>{ //逻辑代码 } ) | 第一个参数el表示指令绑定的元素 第二个参数binding表示指令绑定元素更新后的相关信息 |
4 插槽
插槽的作用:让组件的html结构不写死,也就是让使用者可以自定义这个组件【扩展组件使用的】【使用组件标签内部的html结构就是填充插槽的地方。】
在子组件中写slot标签的地方就相当于在这里挖了个槽,调用的时候将结构放进来【使用slot双标签,slot双标签内部的html结构是插槽的默认内容。使用组件时,组件内部有html结构 就显示使用组件时内部的html结构,没有传值就显示默认内容】【使用组件时用双标签,使用组件双标签内部有html结构,则会将组件内部的html结构放到对应的插槽中去。】
4.1具名插槽
具有名字的插槽就叫具名插槽
为什么要有具名插槽:因为一个组件内部可能需要多个地方不写死,那么就要多个插槽,要多个插槽就需要通过名字区分它们
语法 | 描述 |
<slot name=’目标插槽名’>默认内容</slot> | 子组件中写插槽位置,这句代码写在组件标签内,用于插入需要扩充html结构的地方【创建一个名字为目标插槽名的插槽】 如果你传了html结构就显示你传的html结构,没传就显示默认内容。 |
<组件名> <template v-slot:目标插槽名> 插入的html结构 </template> </组件名> | 父组件中注册组件后,使用该组件就可以将插入的html结构放到对应的插槽中【复用组件时可以插入不同的html结构使得页面更加丰富】【组件名标签内的template标签没有任何意义,只是用于包裹需要插入的html结构】 找到目标插槽名字变量的插槽,并将html结构放到插槽内。 v-solt:可以简写为# 或者将v-slot改为slot=”目标插槽名”【这种方式可以放在任何标签上,但v-slot:目标插槽名只能放在temple标签上。】 |
4.2 默认插槽
没有名字的插槽就叫默认插槽,那么传递时不用写名字会自动给默认插槽【默认插槽其实也有名字,只不过名字叫default】
在使用组件的内部,没用绑定v-sort指令的html结构,就是插入默认插槽的html结构
语法 | 描述 |
<slot>默认内容</slot> | 子组件中写 |
<组件名> 没有被v-sort包裹的html结构 </组件名> | 父组件中使用子组件的时候,里面嵌套的内容会放在插槽中,有v-sort绑定的html结构会放到对应的具名插槽中,没有v-sort绑定的html结构会放到默认插槽中。 |
4.3 作用域插槽
当在父组件中使用子组件标签时,使用组件标签那个桥梁标签内部可以有扩展html结构内容数据,此时html结构中的内容数据可能是从子组件的插槽中来的,所以插槽那里需要暴露数据给桥梁使用【桥梁内嵌的html结构使用】,桥梁里面的扩展内容可以通过绑定的v-slot=自定义对象名的方式将插槽传过来的暴露数据保存在这个自定义对象名里面供嵌套的标签使用
作用域插槽主要是将插槽数据暴露给 使用该组件时对应的内嵌填充的html结构中使用。【使用插槽暴露数据是将数据暴露给填充该插槽的html结构使用。暴露数据是以对象的形式存储】
4.3.1 子组件插槽代码
语法 | 描述 |
<slot :自定义暴露变量名=’子组件data中数据’></slot> | slot标签为子组件的插槽,solt标签中的自定义暴露变量名在使用子组件时可以在内部填充【默认插槽】 |
<slot name=’自定义插槽名’ :自定义暴露变量名=’子组件data中数据’></slot> | 子组件中的具名插槽 |
4.3.2 父组件中桥梁内嵌代码
语法 | 描述 |
<template v-slot=”自定义对象名”>嵌套的html结构</template> | 自定义对象名保存的是子组件插槽暴露出来的数据,属性名为子组件插槽自定义暴露变量名,属性值为自定义暴露变量名在插槽属性中对应的属性值数据。【默认插槽】 |
<template #插槽名=”自定义对象名”>嵌套的html结构</template> | 同上【具名插槽】 |
5 如何使用less
5.1 准备工作
脚手架默认配置是没有支持less的,所以要对项目下包【当然后面通过自定义配置创建vue项目的时候,是可以直接下包,可以不用我们手动通过命令下载】
语法 | 描述 |
npm i less less-loader | 如果项目是npm管理的,就在项目文件夹打开小黑窗输入该命令 |
yarn add less less-loader | 如果项目是yarn管理的,就在项目文件夹打开小黑窗输入该命令 |
5.2 在组件的style标签上写个lang=’less’
语法 | 描述 |
<style lang=’less’ scoped> /* lang=”less”代表用less写样式 scoped代表当前样式只作用于当前页面 */ </style> | 使用lang=”less”后,里面的代码就可以使用less语法编写了。 |
6 v-model在组件中的使用
$event在使用组件时的行内js中表示子组件通知父组件时传递过来的数据,在原生标签中表示事件对象
当父组件使用子组件时,如果父组件只需要暴露一个数据给子组件使用,就可以给使用组件 用v-model的方式绑定暴露数据,子组件中props配置向父组件要value属性,并通过this.$emit(‘input’,参数)的方式通知父组件。【其他的可以继续用:数据名去暴露使用组件的数据】
组件语法 | 描述 |
<子组件 v-model=’数据变量’/> | 等价于 <子组件 :value=’数据变量’ @input=”数据变量=$event” /> 可以理解为 父组件暴露了value属性给子组件内部可以直接使用,子组件只要调用this.$emit(‘input’,参数),就可以修改父组件暴露出去的变量值 |
表单元素语法 | 描述 |
<input v-model=’数据变量’/> | 等价于 <input :value=’数据变量’ @input=’数据变量=$event.target.value’ |
6.1 扩充
高级的程序员,考虑的不是怎么实现一个项目,而是去做一个轮子【写一套框架、插件】来方便手下的人快速开发
element-ui是一套别人封装好的组件库
7 虚拟DOM
虚拟dom就是在vue中,会有一个对象来表示界面中的真实dom
真正的dom就是真正在页面上显示的dom元素
因为vue的复用机制,他会先根据最新的数据构建一个新的dom图,然后跟旧的dom图进行比较,既然要比较,问题来了,如果用真实dom比较可能比较297次左右,非常耗时。所以vue提供了一套虚拟dom,里面只记录这个标签真正用到了哪些属性,然后再进行比较,就能大大的提高效率【新的跟旧的可复用元素比较不一样的地方单独修改。】
7.1 vue的就地复用策略
他会最大限度的复用能用的元素【在已有元素盒子的基础上进行增加或删除盒子,然后添加对应的类名和内容】【因为能复用的话,就不用销毁和创建,性能会更好】
当vue发现你界面要出现一个新的元素时,它会优先考虑有没有能复用的,如果能复用就直接拿来复用,只是改掉不同的属性【行内属性和内容】,这就是vue就地复用的原则
复用 | 描述 |
v-if v-else if条件分支创建的元素 | 只会创建一次,条件改变,显示对应的元素,但不会销毁以前分支的元素,而是在以前元素的基础上,改掉类名和内容而已 |
标签类型相同,就不会销毁,而是直接复用,把类名和文字变成需要的即可 | |
标签类型不同,就会销毁,重新创建新标签。 |
8 diff算法【也称地府算法】
从根元素开始比较,类型一样复用,不一样直接删除重建
一级比完往下级比较,同级之间一级一级进行比较
无key按顺序尝试就地复用【类型一样才复用,不一样不复用】
有key根据key来进行复用【复用的效率大大的提高】
就是一套用来比较新、旧虚拟DOM之间有哪些不同点的算法
先产生新旧虚拟dom,根据key比较,还是就地更新
有key但是key为下标跟没有加key没什么差别,都是按顺序
旧构虚拟DOM,新构虚拟DOM进行比较,然后给旧的虚拟DOM更新哪些内容补丁。
8.1 key的作用
方便在diff算法中快速找到复用项
提升性能、减少性能开销
能复用时更加精准
所以之前才建议v-for要写key,因为提高性能【但是key要给唯一值,性能才提高的比较明显,否则跟就地复用没差别】
9 生命周期
从诞生到死亡经历的一系列阶段就叫生命周期【生命周期钩子写在vue实例的data平级位置】
9.1 Vue的生命周期
是指Vue实例从创建到销毁要经历的一系列阶段
在vue的项目,所有的组件都是vue实例
也就是说一个.vue文件【组件】就是一个vue的实例
new Vue() 中Vue是一个构造函数,new Vue就可以通过这个构造函数实例化一个vue对象【组件】
生命周期钩子,其实就是一个回调函数【在Vue实例到达某个阶段会自动调用的回调函数】
9.2 初始化阶段的钩子
beforeCreate:数据初始化前【创建前】,还无法访问到数据和方法【一般不用,除非以后做SSR的项目】【SSR项目:服务端渲染项目】
created:初始化后【创建后】,最早能访问到数据和方法的地方,用的多,因为可以在这个钩子里做数据的初始化【页面准备数据的代码写在这里】【发请求,拿到数据结果,赋值到data里的数据】
语法 | 描述 |
beforeCreate(){ } | 写在和data和methods同级,初始化前调用,还无法访问到数据和方法,一般不用,除非以后做SSR【服务端渲染】的项目 |
created(){ } | 数据注入后调用,页面就可以根据数据来创建dom元素了。初始化后【创建后】,最早能访问到数据和方法的地方【用得多,因为可以在这个钩子里做数据得初始化,准备页面初始数据】 |
9.3 挂载阶段的钩子
beforeMount:挂载前【渲染前】准备渲染
mounted:挂载后【渲染后】,最早能访问到真实dom的钩子
语法 | 描述 |
beforeMount(){ } | 挂载前【渲染前】准备渲染【还无法访问到真实DOM里面的数据】 |
mounted(){
} | 挂载后【渲染后】,最早能访问到真实dom的钩子 |
9.4 更新阶段的钩子
beforeUpdate:数据改变调用【数据已经改变,但还没有渲染到页面】
虚拟DOM进行diff比对找出不同点【造出要给界面打的补丁】,然后更新到界面
updated:数据改变,且dom更新调用【数据已经渲染到页面了】
这两个钩子一般不用,因为每次跟新都会调用,会有性能问题。
语法 | 描述 |
beforeUpdate(){ } | 跟新前的钩子,仅仅只是数据改变就立即调用的钩子,dom未跟新【数据没有在页面上跟新】 |
update(){ } | 更新后的钩子,当数据发生改变,而且dom得到更新【数据已经在页面上跟新】调用的钩子 |
9.5 销毁阶段的钩子
beforeDestroy:销毁前,还能访问到子组件
destroyed:销毁后,访问不了子组件【已经销毁了】【解除数据侦听、销毁子组件。以及移除事件监听等】
语法 | 描述 |
beforeDestroy(){ } | |
destroy(){ } |
9.6 既有父组件又有子组件时,生命周期调用顺序
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted->谁的数据改变就调用beforeUpdate和updated,此时如果销毁父,调用父的beforeDestroy->子的beforeDestroy->子destroyed->父destroyed
9.7 表格总结
钩子名 | 钩子特点 |
beforeCreate | 最早的钩子,但是一般不用 |
created | 最早能访问数据的钩子,用来做数据初始化准备,一般发送请求写在这里,包括开定时器【动态根据数据创建dom元素的初始化请求数据写在这】 |
beforeMount | 渲染前,没什么用,还无法访问真实dom,一般不用 |
mounted | 最早能访问到真实dom的钩子,可以用来做需要依赖dom的一些初始化,例如echarts |
beforeUpdate | 跟新前,数据发生改变,但是还没更新,一般不用 |
updated | 更新后,数据发生改变,而且dom跟新了,一般不用 |
beforeDestroy | 销毁前,还能访问子组件,可以在这里做一些回收工作【回收vue之外的东西】,例如停止定时器 |
destroyed | 销毁后,不能访问子组件,可以在这里做一些回收工作【回收vue之外的东西】,例如停止定时器 |
9.7 生命周期阶段记录
实例化一个vue对象【组件】——> 初始化基础事件以及生命周期钩子初始化和准备——> beforeCreate()【最早的钩子,我们也叫初始化前】——> 初始化注入与依赖【相当于把data所有数据,还有methods里面的方法,还有computed里面的计算属性注入到当前vue实例身上】【把vue里的数据和方法注入到当前vue实例身上】——> created()【最早可以访问数据和方法的钩子】【一般用于发请求请求数据】——> 判断是否有el属性,没有就用$mount找【找某个dom元素交给vue管理,它的流程是有el用el找,没有el用$mount找】——> 如果有template属性,就把template里写的内容交给渲染函数去渲染,渲染到el或者$mount找到的元素上【如果没有template属性,编译用el找到的盒子里面的html内容作为template属性去渲染】【平时就是没有template属性的vue组件实例,直接渲染el找到的盒子】——> beforeMount【挂载前,渲染前,准备渲染,一般不用】——> 渲染函数调用完毕,也就是说界面已经渲染完了——> mounted()【挂载后,渲染后,是最早能访问到真实dom的钩子】【写echarts,可能页面一打开就要有图表,所以意味着要尽早创建echarts对象,但是创建echarts对象时需要传入一个dom元素作为容器,因为dom元素作为容器,所以再早也只能写到mounted里面】——> beforeUpdate()【更新前的钩子,仅仅只是数据发生变化就立即调用的钩子,数据还是以前的数据】【页面数据更新前就调用,有性能问题,一般不用】——> 虚拟dom进行diff比对,找出不同点【找出要给界面打的补丁】,然后更新到界面——> updated()【更新后的钩子,当数据发生改变,而且dom得到更新调用的钩子】【页面数据一更新就调用,有性能问题,一般不用】——> beforeDestroy()【当vue实例要销毁了,销毁前调用的钩子】【还能访问子组件】——> 解除watch数据侦听、销毁子组件以及移除事件监听等【父组件销毁前会先销毁所有的子组件】【只是移除了Vue的东西,其他东西没有移除,比如定时器】——> destroyed()【销毁后,这个钩子一调用完就代表当前vue实例没了】【只是移除了Vue的东西,其他东西没有移除,比如定时器不能访问子组件】
9.8 钩子应用场景
created用来发请求准备数据,mounted写echarts,beforeDestroy或者destroyed用来回收定时器
10 动态组件
作用:在某个区域不写死,可以动态的切换不同的组件来显示【路径不会发生改变】
简化 多个组件选一个【v-if】使用的
应用场景:组件切换
10.1 语法
以前的写法 | 描述 |
<组件1名 v-if=” 数据变量==’组件1名’ ”/> <组件2名 v-else-if=” 数据变量==’组件2名’ ”/> <组件3名 v-else=” 数据变量==’组件3名’ ”/> | v-if确定 数据变量确定显示哪个组件 |
动态组件语法 | 描述 |
<component :is=”组件名变量” /> | 当组件名变量为哪个组件名,就切换哪个组件 |
10.2 组件缓存
默认情况下,组件要是切走了就销毁了,可能导致一个问题【例如我们上面写了登录,输入了账号密码,不小心点了别的地方走了,再回来内容就没了】
所以我们应该让组件不销毁,就是把组件缓存起来
10.2.1 语法
语法 | 描述 |
<keep-alive> //要缓存的组件 <子组件名 /> </ keep-alive> | 加了缓存它会多两个另外的钩子 activated:当子组件显示时调用 deactivated:当子组件隐藏时调用【隐藏说明以前的数据还在】 加了缓存,该组件的create钩子执行一次后就会失效【创建组件后缓存起来,没有被销毁】,beforeDestroy和destroyed钩子会失效 |
11 单页面应用
以后你们的工作内容,就是每天去SPA【也可能遇到spa+ssr】【ssr:服务器渲染】
单页面应用要依赖一个技术实现:这个技术叫“路由”
11.1 SPA:
Single Page Application【单页面应用】
11.2什么叫单页面应用
就是整个网页项目只有一个页面【看起来的网页跳转,其实只是界面的一个区域里做内容切换】
11.3 为什么要用单页面应用
节约性能;
减少对服务器的请求,就能减轻服务器压力;
切换页面时非常丝滑【第一次首屏加载将所有数据存到了内存中,只需发一次请求】
11.4 缺点:
第一次加载【首屏加载】慢;
不利于seo【搜索引擎优化】
12 路由
前端要通过路由才能实现 单页面应用 的切换效果【小区域组件切换用动态组件,大区域用路由,动态组件路径不会发生变化,路由路径会跟着组件切换改变】
12.1 生活中的路由器
ip地址与设备的对应关系,给每个设备一个ip地址,然后就可以让每个设备上网了
12.2 前端路由
实现一个路径跟一个组件的对应关系
Vue框架的路由是spa【single page application】路径管理器,vue的单页面应用是基于路由和组件的,路由用于设定访问路径【将路径和组件映射起来】
说白了,就是前端路由可以实现一个路径对应一个页面,你跳转这个路径,就会切换到对应的页面【子路由里面的数据会在父路由中】
传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系
至于我们为什么不能用a标签,这是因为用Vue做的都是单页应用【当你的项目准备打包时,运行npm run build时,就会生成dist文件夹,这里只有静态资源和一个index.html页面】,所以你写的标签是不起作用的,你必须使用vue-router来进行管理
打开项目看到的都是App.vue,所以还得把路由对应组件显示到App.vue,所以要提供一个路由出口,也就是<router-view/>
路由出口的意思是指:让路由最终出现在哪个位置
12.3 为什么把路由的代码放到router/index.js里
为了利于阅读和维护【简洁main.js中的代码量,防止main.js文件的臃肿】
13 vue-router基本使用
vue-router是vue提供的一款实现路由功能的插件
简单来说它就是实现路径与组件对应的一款插件
一个项目只有一个路由器【路由信息:this.$route【路由对象】 路由页面:this.$router【路由器对象】】
13.1 创建新项目
语法 | 描述 |
vue create 项目名 | 在项目文件夹里进入小黑窗并输入该代码会创建一个vue项目 |
13.2 添加路由【当默认创建项目时是不包含路由的,需要手动添加路由】
以后添加路由都不需要我们自己加了,因为我们选择自定义创建项目,可以创建时自动就有路由。
语法 | 描述 |
vue add router | 进入项目文件夹并打开小黑窗输入该命令,他会帮我们自动下载vue-router这个插件,帮我们把路由的基本代码写好,并在项目的src源文件下面里创建了一个views的文件夹并在创建好的views文件夹下面创建一个index.js文件,并导入基本代码。【会自动根据我们的vue版本下载对应的路由】 |
当初始化项目时小黑窗看见 “Use history mode for router ?” 我们需要输入n,表示用hash模式
13.3 导入路由【views/index.js】
语法 | 描述 |
import Vue from 'vue' import VueRouter from 'vue-router' | 导入Vue和路由 |
13.4 安装路由【views/index.js】
语法 | 描述 |
Vue.use(VueRouter) | 安装路由 |
13.5 导入组件
语法 | 描述 |
import 路由组件名 from '路由组件路径' | 用于路由规则中的跳转使用 |
13.6 准备路由规则【也叫路由表或者路由记录】【views/index.js】
嵌套子路由 对应的组件 应该在父组件 路由视图窗口中
路由表中单条路由规则【即单个路径上】有多少个组件,就可以有多少个路由出口,路由出口使用name属性指定。
语法 | 描述 |
const routers = [ { name:’自定义名’, path:’/资源路径’, component:路由组件名 } ] | 网址的 资源路径处输入path后面的资源路径就会在路由出口处显示路由组件名对应的路由组件 一级路由就是一个完整的页面所在处 二级路由对应的组件会显示在一级路由对应的组件的页面上【页面某个区域的元素更新可以使用路由嵌套】 |
13.6.1 单个路由路径承载多个路由页面
语法 | 描述 |
const routers = [ { name:’自定义名’, path:’/资源路径’, components:{ 自定义1视图名:路由1组件名, 自定义2视图名:路由2组件名, … } }, … ] | 在显示当前路由组件的地方使用 <router-view name=”自定义1视图名”> <router-view name=”自定义2视图名”> 这种方式可以将多个组件在同一个页面显示 |
13.7 实例化路由对象并使用【views/index.js】
语法 | 描述 |
const router= new VueRouter({routes}) | routers就是13.6 的路由规则 router是我们实例化的路由对象 |
13.8 暴露路由实例化对象【views/index.js】
语法 | 描述 |
export default router | 将路由实例化对象暴露出去 |
13.9 入口文件导入Vue和主文件和路由index.js文件【src/main.js】
脚手架很聪明,你如果导入只写一个文件夹名字,他会自动找到这个文件夹里的index.js或者index.vue文件。【优先找index.js找不到就找index.vue】
语法 | 描述 |
import Vue from 'vue' import App from './App.vue' import router from './router' | 将路由index.js文件导入进来 导入暴露的路由对象router |
13.10 入口文件将路由实例对象挂载起来【src/main.js】
语法 | 描述 |
new Vue({ router, render: h => h(App) }).$mount('#app') | 将路由实例化对象挂载到Vue实例对象上【只有挂载了,每个组件才具有路由的功能】 |
13.11 路由出口【也叫路由视图容器】
路由出口:路由最终出现的位置【要有显示路由切换的组件】【通过页面导航(router-link)或编程导航显示的页面,是通过router-view来承载显示的】
我们人为的把组件分为两大类:页面组件【大组件】、布局组件【小组件,也叫功能组件,复用组件】
因为打开项目看到的都是App.vue,所以还得把路由对应组件显示到App.vue【显示在App.vue组件中的路由组件为一级路由】,所以要提供一个路由出口,也就是<router-view />【App.vue中的 路由出口 为 路由规则中的一级路由路径 对应的页面; 路由规则中的二级路由,路由出口会放在其对应的一级路由路径对应的组件中去。】【同一块区域变化的路由级别相同】
语法 | 描述 |
<router-view /> | 需要显示路由组件的页面添加 |
13.12 以前使用路由的步骤
步骤 | 描述 |
1 | 在src下准备一个router文件夹,里面放index.js |
2 | 导入vue |
3 | 导入路由 |
4 | 安装路由 |
5 | 导入组件 |
6 | 创建路由规则 |
7 | 创建路由对象并使用规则 |
8 | 暴露路由对象 |
9 | 来到main.js导入上面暴露的路由对象 |
10 | 在new Vue挂载路由对象 |
11 | 来到App.vue设置路由出口 <router-view /> |
13.13 组件路由总结
我们人为的把组件分为两大类:页面组件【大组件】、布局组件【也叫功能组件,还叫复用组件,它是个小组件】
以后像这种页面组件【大组件,也是路由组件】放到views文件夹里,像这种复用组件【小组件】放到components里
npm install vue-router下载最新版本路由,就是4.x的路由版本,4.x的路由版本是给vue3用的,但咱们现在是vue2,所以会报错【解决方案:下载指定版本的vue-router——>npm i vue-router@3.x】 【第14个视频】
14 脚手架里的@符号说明
@符号代表直接找到src目录
以后如果在比较深的层级里找到src里的东西,可以用@符号
./当前文件和@自己选择,哪个方便用哪个
15 路由的两种模式
在使用脚手架创建项目的时候,我们有个选项”Use history mode for router?(Requires proper server setup for index fallback in production)” 我们选择输入的的n 【表示不使用路由的history模式,使用路由的hash模式】
15.1 hash模式
hash:路由的默认模式,默认值【特点:网址上有#;网址的资源路径前面要加#】【模式通用,开发和上线皆可使用,无需额外配置】【推荐模式】
语法 | 描述 |
const router = new VueRouter({ routes, mode:’hash’ }) | router为根据路由规则实例化出来的路由对象 routes:路由规则 mode:设置路由模式,默认值为hash,可以不写 |
15.2 history模式
history:网址上没有#,正式上线要服务器支持【这种模式好看一点,但正式上线需要服务器做额外的配置才能正常使用,如果不配置很可能访问页面时得到404】
语法 | 描述 |
const router = new VueRouter({ routes, mode:’history’ }) | router为根据路由规则实例化出来的路由对象;routes:路由规则 mode:设置路由模式,不写默认值为hash,需要将该值设置为history。 |
15.3 总结
hash模式通用,正式上线也可以直接用,而history模式正式上线需要服务器做额外的配置才能正常使用,如果不配置很可能访问页面时得到404
当初始化项目时小黑窗看见 “Use history mode for router ?” 我们需要输入n,表示用hash模式
16 路由声明式导航
本质就是用标签做跳转,专业术语加声明式导航
本质还是a标签【他会根据模式的不同自动给路径添加和删除#号】
没有路由声明式导航时,我们需要在网址上改变url的资源路径才可以实现页面的更新,我们想实现点击某个按钮就跳转到路径对应的页面上,就可以使用声明式导航【我们在组件内部可以通过a标签的类型实现对其样式的更改】
16.1 以前的写法
模式变了,路径需要手动修改,不能实现自动高亮
语法 | 描述 |
<a href=’#/路径’>内容<a/> | hash模式的跳转 |
<a href=’/路径’>内容<a/> | history模式的跳转 |
16.2 声明式导航【路由链接】语法
模式变了,跳转路径不需要我们手动修改,我们只需在to的属性值中写路由规则中的路径即可。【我们要实现a链接的跳转功能,建议使用声明式导航】
语法 | 描述 |
//路径一样的导航才会有那两个类 <router-link to=’网址中的资源路径’>内容</router-link> | 他会根据模式的不同自动给路径添加和删除#号 如果跳转的路径和规则中的目标路径一样,就添加router-link-exact-active和router-link-active两个类名给转换后的a标签 |
16.3 好处
如果是hash模式,会自动给路径前面加#,如果是history就不会在前面#
能实现自动高亮效果。谁被点,它会给被点的router-link加类router-link-exact-active和router-link-active,那我们只要给这两个类设置好样式,就是有点谁谁高亮效果。【router-link-exact-active代表精准匹配,路径一定要给router-link里的to属性完全一样才能匹配,并给router-link转为a标签后的元素添加这个类名;router-link-active代表模糊匹配,也就是说只要路径包含在router-link里的to属性中就能匹配】
17 路由嵌套
就是指一个路由再嵌套一个路由,这就叫路由嵌套
外侧的哪个路由叫父路由,里面的叫子路由
默认的最外层路由都叫1级路由,1级路由里面嵌套的路由就叫2级路由。【看路由是几级路由,可以通过路由规则来看,也可以通过区域改变来看。】【判断是不是1级路由的最大的依据就是,它在不在别的路由里面,只要不在就是1级的,不是通过路径判断,因为路径可以随便定义的】【一般用到二级路由就够了】
如果要给哪个路由加子路由,就是来到路由规则里写children,子路由规则写法是跟父路由规则写法一样,只不过路径一般不加/,这样就代表要拼接父路由的路径再加上自己的路径才能访问。【嵌套子路由 对应的组件 应该在 父组件 路由视图容器里面接收】
当需要给导航路由添加高亮效果时,如果只有一级路由,那么需要使用精确匹配的router-link-exact-active类名来设置样式【保证只有一个导航路由有高亮效果】;如果有路由联动效果【一级路由导航下面有二级路由导航】,且一级路由导航的高亮效果需要和二级路由高亮效果关联起来,这个时候一级路由需要使用模糊匹配的router-link-active类名设置和二级路由需要使用精确匹配的router-link-exact-active类名样式【二级路由声明式导航的跳转路径比一级路由声明式导航路径多了一层/,所以匹配不是精确匹配,精确匹配只有 声明式导航跳转路径和url资源路径一样 样式才会生效,模糊匹配只要声明式导航跳转路径包含在url资源路径中,样式就可以生效。】
语法 | 描述 |
const routes=[ { //一般用组件名作为name属性 name:”组件1名”, //写在url的资源路径位置 path:”/自定义路径1”, //资源路径输入path对应的自定义路径就跳转到这个组件名对应的组件 component:组件名 //设置子路由 children: [ //写法跟外面父路由也是一毛一样 //子路由路径一般不加/【不加/ 路径默认会拼接到资源路径中父路由路径后面 加了/资源路径就是本身】 { path: '自定义路径2', name: 'MyLike', component:MyLike } ] }, { name:”组件2名”, path:”/自定义路径”, component:组件名 }, … ] | 当资源路径与path后面使用的自定义路径相同的时候,页面跟新到component后面使用的组件名对应的组件 当path后面的属性值为*时,需要写在该级别路由的最后一个位置,代表除了其上面路由规则中的所有路径外,其他所有路径都跳转到某个组件中。【因为404页面无需我们点击跳转,所以无需声明式导航。】 |
18 路由重定向
重定向是指,在地址栏的资源路径输入a按回车后能自动跳转到路径b,且资源路径变为路径b。
写一条路由规则
18.1 语法
语法 | 描述 |
const routes = [ {path:’/路径1’,redirect:’/路径2’} ] | 当地址栏的资源路径中输入路径1后按回车,资源路径会自动变为路径2 路由重定向写在路由规则的前面 如果路由重定向里面还有子路由,子路由如果是拼接父路由路径,是拼接的父路由的path路径后面,而不是重定向的路径后面。 |
19 项目准备
19.1 第一种方式【以前的】
步骤 | 描述 |
1 | 创建一个项目文件夹 |
2 | 进入项目文件夹并打开小黑窗cmd |
3 | 小黑窗输入vue create 项目名 |
4 | 选择Default([Vue 2] babel,eslint) |
5 | 小黑窗输入 cd 项目名 进入项目文件夹 |
6 | 小黑窗给项目添加路由 vue add router |
7 | 把不需要的文件删掉 |
19.2 自定义【现在的】
步骤 | 过程 |
1 | 创建一个项目文件夹 |
2 | 进入项目文件夹并打开小黑窗cmd |
3 | 小黑窗输入vue create 项目名 |
4 | 按键盘上下键 选择 Manually select features |
5 | 按键盘上下键 空格选中【选项前面的括号内有星号*表示被选中状态】 [Babel【es6->es5 按需导入】,Router【路由】,CSS Pre-processors【CSS预处理】,Linter/Formater【eslint-语法检查】]当数组中的所有插件都被选中后,按回车【空格可以选中和取消】 |
6 | 上下键选择2.x【表示使用vue2.x版本】,按回车 |
7 | Use history mode for router?后面输入n【表示不使用历史模式,就会使用hash模式】,按回车 |
8 | 选择Less【css预处理插件】, 按回车 |
9 | 上下键选择 ESLint +Standard config【表示使用ESLint语法检查的标准模式,这样可以规范我们的代码】 |
10 | 选中Lint on save【在保存时校验语法是否规范,选这个】 |
11 | 选择In dedicated config files【你在哪放babel、eslint这些插件的配置,用独立的文件做它们的皮质,所以建议选第一个即这个。】 |
12 | Save this as a preset for future projects?我们输入n【表示不保存,不需要用于下一次创建直接使用】【如果输入了y,后续不想要有这个直接使用项,需要在C:\Users\自己用户名\.vuerc去修改】 |
20 路由总结
点击按钮看到一个新页面:使用路由导航【使用跳转一级路由:一级路由其实就是一个页面,里面没有路由出口什么的,我们只是用了声明式路由导航】
看到一个页面中的界面某个区域随着按钮点击而改变【使用二级路由,一个页面界面跟新的地方使用路由出口标签:<router-view />】
20.1 路由组件中的方法【安装路由后】
语法 | 描述 |
$router.back() | 返回上一步【返回上一页】,相当于原生的history.back() |
$router.push(‘/指定路由路径’) | 跳转到指定路由路径的路由。 |
21 路由导航
21.1 声明式导航
模式变了,跳转路径不需要我们手动修改,我们只需在to的属性值中写路由规则中的路径即可。【我们要实现a链接的跳转功能,建议使用声明式导航】
语法 | 描述 |
//路径一样的导航才会有那两个类 <router-link to=’网址中的资源路径’>内容</router-link> | 他会根据模式的不同自动给路径添加和删除#号 如果跳转的路径和规则中的目标路径一样,就添加router-link-exact-active和router-link-active两个类名给转换后的a标签 |
21.2 编程式导航
$router用来做跳转【$router.push(‘要跳转的路径’)】,$route用来做取值【$route.query.属性名】。
有些时候,我们需要逻辑判断,满足条件才做跳转,这是声明式导航做不了的
语法 | 描述 |
$router.push(‘要跳转的路径’) | Vue的行内js代码 |
this.$router.push(‘要跳转的路径’) | Vue的methods里面的方法js代码【该路径需要和路由规则path后面的路径一致。】 |
21.2.1 编程式导航完整写法
query方式传参path跳转语法 | 描述 |
this.$router.push({ //要跳转的路径 path:’/路由对应的路径’, //传递query参数 query:{ 属性1名:属性1值, 属性2名:属性2值, … } }) | 路由规则中的path属性对应路由路径,component属性对应跳转的路由组件名, 跳转到的这个组件里面就可以使用$route.query.属性名的方式获取请求参数的属性【在vue的插值表达式或行内js中】。 左边的语法只是query方式传参【query方式传参会将传的参数使用query字符串的方式拼接到url路径后面且使用?分隔】还有一种params方式的传参方式【传的参数不会拼接到url路径后面】。 |
query方式传参name跳转语法 | 描述 |
this.$router.push({ //要跳转的路径名对应的组件 name:’路由名’, //传递query参数 query:{ 属性1名:属性1值, 属性2名:属性2值, … } }) | 路由规则中的name属性对应路由名,component属性对应跳转的路由组件名, 跳转到的这个组件里面就可以使用$route.query.属性名的方式获取请求参数的属性【在vue的插值表达式或行内js中】。 |
22 路由传参方式
很多时候我们跳转到另一个路由是需要携带一些参数【例如,携带id,id不同,渲染对应的数据】
携带参数有两种方式:query方式、params方式
22.1 query方式语法
22.1.1 语法
语法 | 描述 |
$router.push({ name:’路由名字’, query:{ 属性1:数据1, 属性2:数据2, … } }) | 取值:跳转的组件内部使用$route.query.属性名【组件内部的行内js和插值表达式中】获取参数 this.$route.query.属性名【组件内部的methods中的方法中】获取参数 |
22.2 params传参
22.2.1 语法
语法 | 描述 |
$router.push({ name:’路由名字’, params:{ 属性1:数据1, 属性2:数据2, … } }) | 某个按钮触发的事件里面有该代码,当按钮事件触发后,会将参数name属性对应的路由名字到路由规则中去匹配对应的路由【会将这里的参数params中的属性放到路由规则中path路径后面的冒号中】并跳转到对应的组件中。 跳转到的组件内就可以使用$route.params.属性名的方式获取参数值。 |
22.3 为什么建议携带参数时用name跳转
因为路由编程式导航如果用path,路由不支持获取params参数【路由对应的组件通过插值表达式{{$route.params.属性名}}获取不到】,但是用name既支持获取query参数也支持获取params参数
22.4 query和params方式的区别
query方式 | params方式 |
query传的网址上会能看到参数 | params传的网址上看不到参数 |
query传的参数在网址上,所以一刷新还在 | params的参数是在内存中,所以一刷新就没了 |
query刷新不管之前是什么数据都会变为字符串 | params是直接不能刷新,刷新数据就没了【页面插值表达式获取的数据自然也会消失】。 |
query传的参数可以通过$router.query.属性名的方式获取参数 | params传的参数可以通过$router.params.属性名的方式获取参数 |
23 动态路由匹配
这种方式能限制访问这个路由时必须要传参数,不传就不能访问到
特点:它必须要改路由规则,而且必须要传,不传就访问不了
以后但凡在path路径上看见:变量名;冒号后面的变量名代表一个必传参数
23.1 语法:
语法 | 描述 |
{name:’路由名’,path:’路由路径/:动态参数名’,component: 组件名} | 跳转到的组件中取动态参数值:$route.params.动态参数名【通常需要该参数发请求获取渲染数据,写到当前组件的created钩子中,进入页面就获取到数据。】 |
{name:’路由名’,path:’路由路径/:id’,component: 组件名} | 这里的:id不是说路径叫:id,而是这这里要传递一个id参数过来路由路径后面的id必传 跳转到的组件中取值:$route.params.id |
24 全局前置守卫
全局:所有路由都能触发
前置:进入路由之前
守卫:在代码中是拦截下的回调函数
写了路由前置守卫,默认是不放行的,所以所有页面都看不到。
如果只有from的路由路径匹配
只有将next(‘跳转到指定路由’)嵌套在to.path路由路径匹配的条件中,匹配成功后才会执行一次并且跳转到指定的路由中。【否则会一直循环执行next(‘指定路由路径’)上下的代码块,爆栈后执行next(‘指定路由路径’)后面的代码块】
根据上面可知,以后我们需要使用跳转到指定的路由,就需要将next跳转指定路由放在具有to.path的条件判断里面【所以我们可以设置一个白名单,直接可以使用to.path是否在白名单里面】
语法 | 描述 |
router.beforeEach((to,from,next)=>{ if(to.path===’路由路径’){ next(‘跳转的指定路由’) }else{ //还可以有其他条件判断后放行 next() } })) | router是router的实例化对象 to:去哪个路由的路由组件信息 from:从哪个路由组件来的 next:是一个放行函数,调用next()就代表放行,不调用代表不放行【不放行就跳转的组件就是个空页面,参数可以是路由路径】 next(‘路由对应的路径’):跳转到指定路由。【放行到指定路由】 它在组件创建之前就调用了【beforeCreate钩子前面调用】 |
24.1 使用全局前置守卫完成访问权限控制
不是所有页面都需要登陆才能访问,例如登录页、注册页
也就是说,我们要建立一个白名单列表,只要处于白名单里面的,就是免检产品【不用登陆就能访问】
25 路由懒加载&异步组件
默认情况下打包的所有路由它对应的js和样式都会打包到一个js文件和一个css里,并且在页面一打开时就加载了【这样做的好处:1.切换时非常丝滑;2.大大减少对服务器的请求次数】【这样做的坏处:初次加载慢(首屏加载慢)】
可能有些项目非常注重首屏时间,希望用户越快打开首页越好,就希望不能用这种打包到同一个文件里的行为,所以这时候就要用路由懒加载,也叫异步组件
25.1 路由懒加载语法
将以前的import 组件 from ‘路径’ 改成如下形式就是路由懒加载了
语法 | 描述 |
const组件名= () => import('@/views/组件名.vue ') | 路由规则所在的index.js文件中将导入组件的代码改为这种形式 |
26 ESLint的标准规范说明与插件推荐
默认配置的ESLint只是一些默认规范,里面规范比较少
而现在我们用的ESLint使用了标准规范,所以规范比较多
26.1 常见的ESLint规范
可以在小黑窗使用yarn lint【npm包管理就使用npm run lint】
规范 |
JS字符串要用单引号,不要用双引号 |
建议每句话结尾不要加分号 |
=号两边要有且只有1个空格 |
函数小括号两边有且只有1个空格 |
不要有过多的无意义空格和换行【比如每句话末尾多空格或换行】 |
v-for创建动态元素的时候,动态元素必须加key属性【key值只接受字符串和数值类型】 |
字面量值【null和undefined除外】用===全等比较 |
null,undefined使用==双等比较 |
组件里不能有数字 |
组件名字遵循大驼峰命名法【最少两个单词】,组件暴露的默认name属性可以为大驼峰命名法,也可以为多个单词使用连字符号-连接起来的单词组合。【不写name属性,默认情况下name属性值为组件名。】 |
26.2 插件推荐
插件名 | 描述 |
ESLint | Vscode默认是不检查你语法规范的,也就是说你多敲了几个空格或者单引号变双引号都不符合规范,但是vscode不会报错,要看小黑窗才知道报没报错 装了这个插件后,vscode就会有报错提示了。 |
vscode-icons | 让vscode有图标 |
Prettier-Code formatter | 保存自动格式化代码,防止eslint报错。【eslint格式化失效,可以用脚手架自带功能。npm包管理工具使用npm run lint或yarn包管理工具使用yarn lint】 |
27 如何运行代码
node_modules是依赖包文件夹,里面全是碎文件,下载很慢,所以每个仓库都把node_modules这个文件夹忽略掉了
步骤 | 过程 |
1 | 根目录打开命令行,如果是npm包管理工具 要先npm i把依赖包下载下来【如果用的yarn包管理工具就使用yarn把依赖包下载下来】,然后才能运行项目 |
2 | 命令行输入npm run serve【根据package.json文件来】 |
28 Vue.use的原理
其实就是在调用对象的install方法,如果传入函数就是在调用这个函数
只不过一般我们都是在Vue.use里传入一个对象,调用这个对象的install方法
一般情况下install方法都是在注册全局组件,或者给原型挂载方法。
28.1 注册组件方法
语法 | 描述 |
// 导入所有组件 import 组件名 from '组件路径' export default { // install被调用时会有一个参数,加vue install (Vue) { Vue.component('组件名', 组件名) } } | 以后要使用该组件 需要导入该文件的路径并默认用一个变量来接收 Vue.use(变量名)即可 |
29 Vant
作用:快速利用的移动端的布局
组件库:组件的仓库,提供了大量组件供开发者使用
组件是什么:组成网页的一部分
29.1 下载vant
进入到项目文件夹,在根目录【package.json平级处】打开cmd
语法 | 描述 |
npm i vant@latest-v2 -d | 下载vant到依赖包 |
npm i vant@latest-v2 -f | 如果-s方式下载不了,使用这种方式下载vant |
29.2 vant的全局导入【无需掌握】
不推荐,https://vant-contrib.gitee.io/vant/v2/#/zh-CN/button官网自己看
29.2.1 特点
一次导入,想用什么就可以直接使用
优点:方便,极其方便
缺点:不管你用到的还是没用到的,它会帮你打包导入到项目里,会增大项目体积,就以为访问速度会变慢一点
29.3 vant的按需导入【无需掌握】
不推荐,https://vant-contrib.gitee.io/vant/v2/#/zh-CN/button官网自己看
29.3.1 特点
用什么导入什么
优点:减少项目体积
缺点:麻烦,极其麻烦
29.4 vant的自动按需导入
先下载vue2的vant npm i vant@latest-v2 -S –force
步骤 | 过程 |
1 | 下载插件 npm i babel-plugin-import -D -f |
2 | 来到babel.config.js做如下配置: module.exports = { plugins:[ ['import', { libraryName:'vant', libraryDirectory: 'es', style: true }, 'vant'] ] }; |
3 | 在main.js中引入需要的组件,并安装上,比如button按钮 import { Button } from 'vant'; Vue.use(Button); |
4 | 在哪个组件需要使用就在哪里导入标签结构 <van-button type="primary">主要按钮</van-button> |
30 项目里api文件夹和utils文件夹说明
以后多个文件需要使用的代码块可以放在这两个文件里面【可以大大提高代码复用的效率】
30.1 utils【工具文件夹】
以后里面放一些自己封装的工具函数
和对第三方插件的导入【比如vant.js,放所有vant.js有关的代码,后面导入到main.js入口文件即可】
30.2 api【接口文件夹】
以后在这里封装一些发送请求调用接口的代码
好处:以后万一这个接口多个项目要用,我直接复制api文件夹到别的项目就可以
30.3 封装api接口-注册接口
把接口封装到api文件夹,是为了利于修改、复用
然后还会根据模块的不同,新建不同的js文件用来专门封装这一个模块里的请求。
40 vant中使用vw屏幕适配的方案
Vant默认使用px作为样式单位,如果需要使用viewport【视口】单位(vw,vh,vmin,vmax),推荐使用postcss-px-to-viewport进行转换
Postcss-px-to-viewport是一款PostCSS插件,用于将px单位转化为vw/vh单位
移动开发的时候,美工给的设计稿是px单位的
40.1 下载Postcss-px-to-viewport插件
语法 | 描述 |
npm install postcss-px-to-viewport --save-dev | 下载将px单位转为vw或vh单位的插件 |
npm install postcss-px-to-viewport --save-dev -f | 上面命令报错【node版本太高】,使用该命令下载 |
40.2 提供Postcss-px-to-viewport插件的配置文件
美工给设计稿,她用一个屏幕作为底稿,屏幕是2倍图,总宽度为750像素,所以折合到屏幕上点应该是375个点。
步骤 | 过程 |
1 | 在项目根目录【package.json同级】新建postcss.config.js配置文件 |
2 | 在postcss.config.js配置文件中写如下代码 |
module.exports = { plugins: { 'postcss-px-to-viewport': { // 设置视口宽度,单位默认是px【设置的px单位宽度会自动转为vw单位】【viewportHeight:高度;设置后会根据高度来将px单位转为vw单位】 viewportWidth: 375 } } } | |
3 | 我们可以自己写一个盒子,就用px单位,运行后看看有没有自动变为vw单位,变了代表适配完成【以后打包或者在浏览器中都可以看到转换好后的vw单位了】 |
41 下载axios【用于封装发送请求的插件】
语法 | 描述 |
npm i axios | 下载axios |
41.1 给axios设置基地址
项目可能有多个基地址,所以最好克隆一个axios实例来设置基地址。如果以后有多个基地址,克隆多个不同的axios实例对象,再设置基地址即可
直接设置基地址 | 描述 |
axios.defaults.baseURL=’基地址’ | 直接给axios基地址,不利于扩展,这样设置项目只会有一个基地址,但以后项目可能有多个基地址 |
创建实例设置基地址 | 描述 |
const request=axios.create({ baseURL:’基地址’, timeout:毫秒数 }) | 克隆一个新的axios实例【克隆了一个和axios一样的对象,并且设置了新对象的基地址】 当需要新的基地址axios实例,可以通过axios继续克隆。 timeout设置发一个请求在指定毫秒数还没反应就直接连接超时。 |
42 storage模块封装
好多接口要携带token,因为这样服务器才好判定你是谁,给你返回对应得数据以及还要根据你的token判断你有没有资格访问这个接口
登陆后,服务器返回了token,而token很多页面都要用,而且token有效期可能比较长,所以最好存到本地存储,这样所有页面都可以读取,而且关掉浏览器token也还在
但是如果哪个页面要用,就需要在哪个页面写localStorage.getItem(‘key’)代码重复了,而且万一key变了,多个地方都需要修改key值,不便于维护
最好得办法就是:封装到一个独立的js文件,谁要用,谁导入即可。
42.1 封装storage模块
来到utils文件夹,新建storage.js,里面放三个封装的方法:保存设置token、获取token、删除token
这样做的目的:方便维护,更好的复用
代码 |
// 常量设置token值,常量改变即可跟新所有token有关方法的值 const TOKEN = 'hmmj-mobile-token' // 保存设置token export const setToken = token => { localStorage.setItem(TOKEN, token) } // 获取token export const getToken = () => { return localStorage.getItem(TOKEN) } // 删除token export const removeToken = () => { localStorage.removeItem(TOKEN) } |
43 vant中tabbar的使用
一个组件只能有一个tabbar【主要用于菜单路由跳转】
43.1 在vant.js文件中导入并安装tabbar标签栏
代码 |
import Vue from 'vue' import { Tabbar, TabbarItem } from 'vant' Vue.use(Tabbar) Vue.use(TabbarItem) |
43.2 在需要使用tabbar的组件内部粘贴官网的html结构
父标签<van-tabbar>的行内属性v-model绑定的active为默认选中的子标签<van-tabbar-item>元素,当子标签元素<van-tabbar-item>没有name行内属性时,active为默认选中的子标签<van-tabbar-item>的索引,active的值为几,则索引为几的<van-tabbar-item>元素就被选中;当子标签元素<van-tabbar-item>有name行内属性时,active为默认选中的子标签<van-tabbar-item>的name属性值,active为什么值,则name属性为什么值的<van-tabbar-item>元素就被选中;
子标签元素<van-tabbar-item>的行内icon属性绑定图标名。
代码 |
<van-tabbar v-model="active"> <van-tabbar-item icon="fire-o">面经</van-tabbar-item> <van-tabbar-item icon="search">收藏</van-tabbar-item> <van-tabbar-item icon="friends-o">点赞</van-tabbar-item> <van-tabbar-item icon="setting-o">我的</van-tabbar-item> </van-tabbar> |
43.3 tabbar的路由模式
使用路由模式后,父组件的v-model属性没有意义,因为路由模式下,点击谁谁就会高亮选中【url资源路径和to属性值一致就会高亮】。【也就是给van-tabbar加route属性;然后给van-tabar-item加to属性】
父标签<van-tabbar>的行内添加route属性,子标签元素<van-tabbar-item>添加to属性,属性值为对应跳转的路由路径,
代码 |
<router-view></router-view> <van-tabbar route> <van-tabbar-item icon="fire-o" to="/article">面经</van-tabbar-item> <van-tabbar-item icon="search" to="/collect">收藏</van-tabbar-item> <van-tabbar-item icon="friends-o" to="/like">点赞</van-tabbar-item> <van-tabbar-item icon="setting-o" to="/user">我的</van-tabbar-item> </van-tabbar> |
44 导入的细节
一切导入都相当于把导入的文件里的代码执行一遍【在哪导入就在哪执行】【他会将导入的文件内容当代码执行一次】,如果需要使用导入文件中的变量,就需要将导入文件中的变量暴露出去,接收导入文件的文件用相同名字的变量去接收。
44.1 模块化的导入语法:commonjs【require】和es6【import】的导入语法
默认情况下,package.json文件中的type节点的属性值为commonjs【"type": "commonjs"】【表示为commonjs语法】,我们需要使用ES6模块化module语法,可以在package.json文件中将type节点的属性值设置为【"type":"module"】
44.1.1 共同点
如果找的文件名写了指定的路径,那就去找指定路径的文件;如果找的文件名没有写路径,先找当前文件所在文件夹的node_modules里的文件夹,找不到再找全局依赖包【没写指定路径,永远都是找node_modules里的文件】。【script导入语法不写指定路径是找当前文件中的文件名,不是node_modules文件夹里的文件了。】
44.2 es6模块化导入导出语法
一个js文件【模块B】里面的变量需要在另一个js文件【另一个模块A】内部使用,就需要在当前模块【模块B】将变量导出【暴露】出去,另一个模块【模块A】导入接收。
默认导出只能写一次,按需导出可以写多次
默认导出【export default {暴露变量列表}】只能使用一个变量去接收【import 自定义对象名 from ‘文件B路径’】,接收的变量会变为一个对象,且对象里面保存了暴露数据的变量名和变量值【变量名为对象中的属性,变量值为对应的属性值。】
44.2.1 默认导入导出【一个js文件就是一个模块】
模块B默认导出语法 | 模块A默认导入语法 |
export default { 变量名, 方法名 } | //自定义对象名中保存了模块B中所有默认导出//即暴露的数据【变量和方法】,import紧邻后//面的自定义对象名 位置处不能写解构语法 import 自定义对象名 from ‘模块B路径’ |
44.2.2 按需导入导出
模块B按需导出语法1 | 模块A按需导入语法1 |
export const 目标变量名=值 | import {目标变量名} from ‘模块B路径’ |
模块B按需导出语法2 | 模块A按需导入语法2 |
目标变量列表初始化代码 export {目标变量列表} | import {目标变量列表} ‘模块B路径’ |
44.3 默认导入和按需导入的判断
当导入语法中,import后面的接收变量加了花括号{}表示按需导入;import后面的接收变量就只是一个变量名,没有花括号{}表示默认导入。
按需导入的接收变量需要和按需导出的变量名保持一致。默认导入的接收变量可以自定义。
45 路径复习
路径符号 | 描述 |
./ | 当前文件所在文件夹中查找【当前】 |
../ | 跳出当前文件夹,在上一级文件夹内查找【上一级】 |
/ | 当前环境下的根目录 |
46 vant 主体定制
46.1 按需引入主体样式
需要先修改两个配置文件babel.config.js和vue.config.js文件,修改好这两个配置文件后【需要手写的地方已经标黄,其他的官网复制】【js文件里面颜色样式属性对应的颜色样式属性值需要加引号;css中颜色样式属性对应的样式颜色样式属性值不加引号】
改配置文件一定要重启才有效
步骤 | 描述 |
1 | 在babel.config.js文件中配置按需引入样式源文件 |
module.exports = { plugins: [ [ 'import', { libraryName: 'vant', libraryDirectory: 'es', // 指定样式路径 style: name => `${name}/style/less` }, 'vant' ] ] } | |
2 | 在脚手架创建的项目的vue.config.js文件中修改样式变量,配置如下 |
module.exports = defineConfig({ transpileDependencies: true, css: { loaderOptions: { less: { // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。 lessOptions: { modifyVars: { // 修改主题的地方 // 直接覆盖变量【这里的属性名是变量】 'text-color': '#111', 'border-color': '#eee' // 修改主题颜色,颜色值需要使用引号包裹【这种方式改动适用于改动比较少的情况如果改动较多,建议使用独立的less文件覆盖。】 主题变量:’颜色值’ } } } } } }) | |
//修改主题颜色那里的代码如果变量较多【或者可以通过 less 文件覆盖(文件路径为绝对路径)】【注意less主题颜色文件中的主题变量需要用@开头,颜色值不能用引号包裹】,可以修改为如下代码【后面的会覆盖前面已经设置好的样式。】 hack: 'true; @import "less主题颜色文件路径";' |
46.2 创建主题颜色文件src/styles/themStyle.less文件写主题颜色样式
案例代码 | 描述 |
@blue: pink; | 将以前是blue设置主题颜色的标签改为粉红色 |
47 vant中的NavBar导航栏
47.1 将NavBar导航栏引入到vant.js文件里面
代码 |
import Vue from 'vue'; import { NavBar } from 'vant'; Vue.use(NavBar); |
47.2 将基础用法的标签结构放入需要使用的组件里面
title行内属性绑定的是中间的标题文字内容,标题可以自定义
left-text行内属性绑定的是左边箭头旁边的文字内容
right-text行内属性绑定的是右侧的文字内容
left-arrow属性写上就表示返回有箭头
根据需求删除和修改
标签结构 |
<van-nav-bar title="自定义标题" left-text="返回" right-text="按钮" left-arrow @click-left="onClickLeft" @click-right="onClickRight" /> </div> |
48 下载借鉴别人写好的接口文档
步骤 | 过程 |
1 | 鼠标右键->检查【或者F12来到控制台】 |
2 | 点击“netWork” |
3 | 选择“Fetch/XHR”小黄人 |
4 | 刷新页面 |
5 | 找到请求的.json接口请求文件 |
6 | 点击该文件 |
7 | 鼠标右键 |
8 | 点击”copy” |
9 | 点击“copy link address” |
10 | 在网址上粘贴复制过来的网址就可以直接下载了。 |
49 vant中的Toast轻提示
Vant提供了一个专门给移动端用的提示,叫toast【轻提示】
49.1 将Toast轻提示引入到van.js文件里
全局引入Toast组件后,会自动在vue的原型prototype上挂载$toast方法,便于在组件内调用。
代码 |
import Vue from 'vue'; import { Toast } from 'vant'; Vue.use(Toast); |
49.2 调用轻提示【组件内部使用】
语法 | 描述 |
this.$toast.sucess(‘成功提示信息’) | 弹出成功提示信息 |
this.$toast.fail(‘失败提示信息’) | 弹出失败提示信息 |
49.3 没有全局导入Toast组件时【组件内部或js文件里用】
在js文件中使用Toast轻提示需要先导入才可以使用。
49.3.1 导入Toast组件
代码 |
import { Toast } from 'vant'; |
49.3.2 使用轻提示
语法 | 描述 |
Toast.sucess(‘成功提示信息’) | 弹出成功提示信息 |
Toast.fail(‘失败提示信息’) | 弹出失败提示信息 |
50 vant的cell单元格
50.1 引入cell
语法 |
import Vue from 'vue'; import { Cell, CellGroup } from 'vant'; Vue.use(Cell); Vue.use(CellGroup); |
50.2 将基础结构复制到组件结构中
代码 |
<van-cell-group> <van-cell to="点击跳转的路由路径" title="单元格" value="内容" /> <van-cell title="单元格" value="内容" label="描述信息" /> </van-cell-group> |
51 vant中的list【上拉加载】
Vant官网先先找到list列表,将结构复制到需要使用list上拉加载的地方【按需导入List的代码放在vant.js中。结构和 js代码放在需要使用的组件中】
51.1 引入list列表
在utlis/vant.js文件中引入list列表
语法 |
import Vue from 'vue'; import { List } from 'vant'; Vue.use(List); |
51.2 将v-for动态创建的元素或组件嵌套在list的基本结构中
v-model绑定的loading,用于标记当前是否正在加载【如果为true表示正在加载,会显示加载中的图标;如果为false,代表当前没有在加载,加载图标会消失,可以调用onLoad方法。所以每次加载完数据都需要手动将loading的值改为false】
:finished用于标记是否全部加载完成。为true表示已经全部加载完成,onLoad方法将不会再被调用。
当onLoad方法中没有调用axios请求时,只有在<van-list>组件内部的元素触碰到当前页面底部的情况下,onLoad事件才可以重新触发【this.loading=false】。onLoad方法会在首屏没铺满一屏的情况下调用【当onLoad方法内部有调用axios请求数据时才有这个效果。】。
代码 |
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" > <标签名 v-for="item in list" :key="item.id" ></标签名 > </van-list> |
51.2.1 onl oad
用来加载数据的方法,页面一打开只要finished和loading都为false,默认就会调用一次,然后如果没铺满一屏会再调用一次,直到铺满就停止【onLoad方法内部有调用axios请求时,它会反复调用finished没执行前的所有代码,当finished=true执行后,执行完finished后续的代码就退出onLoad方法。】【没有axios请求调用时,需要元素触碰到页面底部才有前面的效果,否则,只会执行一次onLoad方法。】,停止后如果你上拉快到底会继续调用。调用onLoad方法会自动把loading的值改为true
代码 |
async onl oad () { // 基于axios请求获取请求数据【页面不会刷新】 const res = await articleListAPI({ current: 1, pageSize: 5 }) // 将请求数据放到渲染数据里【页面不会刷新,但数据在增多】 this.list.push(...res.data.rows) // 调用onLoad方法就会将loading值改为true,所以请求数据后需要它的值改为false,为下次调用onLoad方法做准备。【这句代码需要放在请求后面,因为这样如果还没有请求到数据就可以看到加载中的图标】 this.loading = false // 数据全部加载完成 if (this.list.length > 20) { // 数据全部加载完成,执行完本次代码后就退出onLoad方法 this.finished = true } } |
51.2.2 loading
标记当前是否正在加载
为true代表正在加载,会显示加载中
为false没有正在加载,可以调用onLoad方法
调用onLoad会自动把这个值改为true
默认为false,每次加载完一定要记得改为false,不然不能进行下次加载
51.2.3 finished
标记是否全部加载完成
默认应该是false
每次加载完应该判断是否全部加载完,加载完把它改为true
52 vant 中的Grid宫格
52.1 引入grid宫格
语法 |
import Vue from 'vue'; import { Grid, GridItem } from 'vant'; Vue.use(Grid); Vue.use(GridItem); |
52.2 将结构放入需要的组件中
父组件标签的column-num属性用于指定一行放几列,多的换行继续放。没有给该属性,默认情况下的默认值为4,表示一行放4个宫格。
子组件标签的icon属性用于指定放什么图标
子组件标签的text属性用于指定图标下面是什么文字内容
子组件标签的to属性用于路由跳转
代码 |
<van-grid :column-num=’列数’> <van-grid-item icon="photo-o" text="文字" to="指定跳转的路由路径" /> <van-grid-item icon="photo-o" text="文字" /> <van-grid-item icon="photo-o" text="文字" /> <van-grid-item icon="photo-o" text="文字" /> </van-grid> |
53 项目打包
项目写完了需要压缩代码、把文件转成浏览器认识的html、css、js。但这些不用我们自己做,脚手架已经提供了,我们只要运行命令npm run package.json文件内部的script节点内部vue-cli-service build属性值对应的属性【一般是npm run build也可能是npm run build:prod。还可以是npm run 自定义属性。】
53.1 打包项目
语法 | 描述 |
npm run build | 通过该命令打包项目,package.json的scripts节点内部vue-cli-service build属性值对应的属性是什么,就用npm run 什么 |
53.2 配置相对路径
脚手架打包时默认生成的路径是绝对路径里的‘/’,代表当前环境的根目录,因为脚手架的作者认为你的网站就是放在服务器根目录上的【所以压缩后的项目文件路径是以/开头,/代表当前环境的根目录,而我们打包后的项目并不一定在根目录上面,就有可能报错。】,所以最好使用相对路径,这样不管放在哪运行都不会报错。
package.json文件叫项目信息文件;vue.config.js文件加项目配置文件。
来到vue.config.js配置文件做如下配置
语法 | 描述 |
publishPath:’’ | 不写,默认值为publishPath:’/’,代表打包后的文件为绝对路径以/开头。写publishPath:’./’和publishPath:’’都是代表相对路径打包。 |
53.3 通过网址展示自己的项目
步骤 | 过程 |
1 | https://gitee.com/登陆gitee |
2 | 点击右上角加号图标“+” |
3 | 弹出的弹框中点击“新建仓库” |
4 | 输入相关项目描述信息,点击“创建”【会跳转到另一个页面】 |
5 | 进入到打包好后的项目文件夹“dist”,将它放到没有.git文件的文件夹中 |
6 | 小黑窗进入dist文件夹 |
7 | 输入git init【初始化项目】 |
8 | 输入git add .【将文件上传到暂存区】 |
9 | 输入git commit -m’项目信息记录’【本地存储已经好了,下面与gitee仓库建立链接】 |
10 | 将第4步跳转的新页面最后下面的已有仓库下面的最后两行代码 依次输入命令行中,如下代码: |
$ git remote add origin https://gitee.com/Hello_Vue/my_show.git【与本地仓库建立联系】 | |
$ git push -u origin "master"【将本地项目推送到仓库】 | |
11 | 刷新页面,即可上传成功 |
12 | 点击“服务” |
13 | 点击“Gitee Pages” |
14 | 点击“启动” |
54 路由懒加载&异步组件
默认情况下打包的所有路由它对应的js和样式都会打包到一个js文件和一个css里,并且在页面一打开时就加载了【这样做的好处:1.切换时非常丝滑;2.大大减少对服务器的请求次数】【这样做的坏处:初次加载慢(首屏加载慢)】
可能有些项目非常注重首屏时间,希望用户越快打开首页越好,就希望不能用这种打包到同一个文件里的行为,所以这时候就要用路由懒加载,也叫异步组件
54.1 路由懒加载语法
将以前router/index.js文件中的import 组件 from ‘路径’ 改成如下形式就是路由懒加载了
语法 | 描述 |
const组件名= () => import('@/views/组件名.vue ') | 路由规则所在的index.js文件中将导入组件的代码改为这种形式 |
55 引入图片的三种方式
55.1 在模板中引入本地图片
语法 | 描述 |
<template> … <img src=”图片路径”> </template> | 在模板的html结构中引入 |
55.2 在js中引入本地图片
在模板中使用是:<img :src=”自定义图片属性”>
语法1 | 描述 |
import 自定义使用图片名from '@/assets/my.png' export default { data () { return { …, 自定义图片属性: 自定义使用图片名 } } } | Es6导入 |
语法2 | 描述 |
export default { data () { return { …, ig: require('@/assets/my.png') } } } | common.js导入 |
55.3 在css中背景图片引入
语法 | 描述 |
选择器{ …, background:url(“~@/assets/home.png”); } | Css中背景图片的引入 |
56 组件过渡
56.1 下载animate.css
语法 | 描述 |
npm i animate.css@3.7.2 | 下载组件过渡的css文件 |
56.2 入口文件main.js全局安装animate.css
语法 | 描述 |
import AnimateCss from “animate.css” Vue.use(AnimateCss) | 下载组件过渡的css文件 并安装它。 |
56.3 在需要添加过渡的标签或元素或组件嵌套在transition标签中
animate.css中文网: http://www.animate.net.cn/【这里面去看示例和样式类名】
控制进入方式的类名属性:enter-active-class
控制离开方式的类名属性:leave-active-class
进入和离开都需要有animated类名
mode属性:控制是先进后出还是先出后进【in-out: 先进后出;out-in:先出后进】
语法 | 描述 |
<transition enter-active-class="animated slideInRight" leave-active-class="animated slideOutLeft" > <过渡元素> </transition> | enter-active-class控制进入时,过渡元素的进入方式【创建的进入方式】 leave-active-class控制离开时,过渡元素的离开方式【销毁的离开方式】 过渡元素必须是动态创建的【创建和销毁】 |
57 vue项目中使用echarts
57.1 下载echarts
npm i echarts | 命令行下载echarts插件 |
57.2 在main.js入口文件导入echarts
// 导入echarts import * as echarts from 'echarts' import 'echarts-gl' // 将echarts挂载到原型上 Vue.prototype.echarts = echarts |
57.3 在使用echarts的组件的mounted钩子中使用echarts
optionPie自己到echarts官网配置。
mounted () { // 初始化echarts const myChart = this.echarts.init(this.$refs.pie) myChart.setOption(optionPie) } |
第三章
1 viewport布局【vant中】
Vant默认使用px作为样式单位,如果需要使用view单位【vw,vh,vmin,vmax】,推荐使用postcss-px-to-viewport【postcss-px-to-viewport是一款PostCSS插件,用于将px单位转为为vw/vh单位】
2 项目中用vw进行屏幕适配【vant中】
2.1 下载插件
语法 |
npm install postcss-px-to-viewport --save-dev --force |
2.2 提供配置
来到项目根目录【跟package.json平级】的位置新建postcss.config.js,在该文件里面写如下代码
代码 |
module.exports = { plugins: { 'postcss-px-to-viewport': { viewportWidth: 375 } } } |
2 多个接口设置基地址
语法 | 描述 |
/* 封装axios用于发送请求 */ import axios from 'axios' // 以后开发基本上都不会直接给axios设置基地址,因为这样不利于扩展 // 万一以后项目要有多个接口服务器,就意味着有多个基地址,写给axios不好, axios只能给1个基地址 // 克隆一个新的axios对象 // 并且设置了新对象的基地址 const request = axios.create({ baseURL: '基地址', //设置超时时间 //意思是如果发一个请求超过目标毫秒数还没反应就直接报错404 timeout: 目标毫秒数 }) // 添加请求拦截器 request.interceptors.request.use(function (config) { // 在发送请求之前做些什么 // config.headers.请求头属性 = 携带数据 config.headers.Authorization = `Bearer ${getToken()}` return config }, function (error) { // 对请求错误做些什么 return Promise.reject(error) }) // 添加响应拦截器 request.interceptors.response.use(function (response) { // 对响应数据做点什么,取值时少写一个data return response.data }, function (error) { // 对响应错误做点什么 return Promise.reject(error) }) // 暴露出去 export default request |
3 基地址为什么不直接给axios
一个项目的接口可能不止一个,意味着基地址可能有多个。
4 storage模块封装【封装在utils文件夹下面】
登陆后,服务器返回了token,而token很多页面都要用,而且token有效期可能比较长,所以最好存到本地存储,这样所有页面都能读取,而且关闭浏览器token也还在
如果哪个页面要用,就会在哪个页面写localStorage.getItem(‘token’),代码重复了,而且万一以后token变量变了,我们获取页面token的代码每一个用到的地方都需要变,不方便维护,所以将该方法设置在独立的文件里面,随用随拿,直接导入就好,而且只需要修改一个文件的token变量就可以改变所有需要引入文件的地方代码,方便维护。【封装到一个独立的js文件,谁要用,谁导入即可】
但是如果哪个页面要用,就哪个写localStorage.getItem(token)
4.1 token携带语法
语法 | 描述 |
// 常量的规范是名字大写 const TOKEN_KEY = 'hmmj-mobile-token' // 保存 export const setToken = (token) => { localStorage.setItem(TOKEN_KEY, token) } // 获取 export const getToken = () => { return localStorage.getItem(TOKEN_KEY) } // 删除 export const removeToken = () => { localStorage.removeItem(TOKEN_KEY) } | 封装一个本地存储设置值和获取值以及删除值的方法 |
4.2 为什么要携带token
好多接口要携带token,这样服务器才好判断你是谁,给你返回对应的数据以及还要根据你的token判断你有没有资格访问这个接口
5 复习
5.1 路径复习
路径符号 | 描述 |
./ | 当前 |
../ | 上一级 |
/ | 当前环境下的根目录【如果是服务器打开的就是项目根目录】 |
5.2 默认导出和按需导出复习
当一个js文件只需执行一次,不用暴露变量,只需在需要执行的地方使用代码import ‘路径/文件名.js’导入【文件内部如果需要依赖其他依赖包,也需要在该文件内部导入其他依赖包】
默认导出语法 | 默认导入语法 |
export default 目标数据变量 | import 自定义变量 from ‘路径’ //自定义变量和目标数据一模一样 |
按需导出语法 | 按需导入语法 |
export {变量列表} | import 变量 from ‘路径’ |
6 页面访问权限控制思路
在准备进入到页面,但是还没进入到页面之前,需要拦截下来,判断本次访问是否具备权限,具备权限,就放行【让你访问】,否则就放行到登录页
所以意味着需要在跳转某个路由之前,要有一个守卫一样的角色,拦截下来,有没有?有,那就是全局前置导航守卫。
7 全局前置守卫
全局:所有路由都能触发
前置:进入路由之前
守卫:在代码中是拦截下的回调函数
写了路由前置守卫,默认是不放行的,所以所有页面都看不到。
如果只有from的路由路径匹配
只有将next(‘跳转到指定路由’)嵌套在to.path路由路径匹配的条件中,匹配成功后才会执行一次并且跳转到指定的路由中。【否则会一直循环执行next(‘指定路由路径’)上下的代码块,爆栈后执行next(‘指定路由路径’)后面的代码块】
根据上面可知,以后我们需要使用跳转到指定的路由,就需要将next跳转指定路由放在具有to.path的条件判断里面【所以我们可以设置一个白名单,直接可以使用to.path是否在白名单里面】
语法 | 描述 |
router.beforeEach((to,from,next)=>{ if(to.path===’路由路径’){ next(‘跳转的指定路由’) }else{ //还可以有其他条件判断后放行 next() } })) | router是router的实例化对象 to:去哪个路由的路由组件信息 from:从哪个路由组件来的 next:是一个放行函数,调用next()就代表放行,不调用代表不放行【不放行就跳转的组件就是个空页面,参数可以是路由路径】 next(‘路由对应的路径’):跳转到指定路由。【放行到指定路由】 它在组件创建之前就调用了【beforeCreate钩子前面调用】 |
7.1 使用全局前置守卫完成访问权限控制
不是所有页面都需要登陆才能访问,例如登录页、注册页
也就是说,我们要建立一个白名单列表,只要处于白名单里面的,就是免检产品【不用登陆就能访问】
8 npm下载包老是报错
建议清除一下npm的缓存【也可能是node版本太高。】
8.1 清除语法
语法 | 描述 |
npm cache clean -f | 清除npm缓存 |
9封装api接口-注册接口
把接口封装到api文件夹,是为了利于修改、复用
还会根据模块的不同,新建不同的js文件用来专门封装这一个模块的请求
封装接口 | 描述 |
user.js | 这个文件专门发跟用户模块的请求 |
article.js | 这个文件专门发跟文章有关的请求 |
10 token有效期处理
token是有有效期的,有效期是看后端如何设置的时长
我们要处理一下,如果是token过期,就大会登录页让用户重新登录
token如果出错,会触发响应拦截,但是我又不能任意出错都打回登录页,所以应该判断是不是token错误【如果是token错误,我们的状态码为401】,在js文件中需要使用路由跳转,则需要先引入路由文件【有暴露路由实例router】,然后在token判断里面写router.push(‘路由地址’)。【如果是vue组件的方法中使用this.$router.push(‘路由地址’),可以不用导入 router.js 那个文件。】
11 list组件的基本使用【上拉加载】
Vant官网先先找到list列表,将结构复制到需要使用list上拉加载的地方【按需导入List的代码放在vant.js中。结构和 js代码放在需要使用的组件中】
11.1 onl oad
用来加载数据的方法,页面一打开只要finished和loading都为false,默认就会调用一次,然后如果没铺满一屏会再调用一次,直到铺满就停止,停止后如果你上拉快到底会继续调用
11.2 loading
标记当前是否正在加载
为true代表正在加载,会显示加载中
为false没有正在加载,可以调用onLoad方法
调用onLoad会自动把这个值改为true
默认为false,每次加载完一定要记得改为false,不然不能进行下次加载
11.3 finished
标记是否全部加载完成
默认应该是false
每次加载完应该判断是否全部加载完,加载完把它改为true
11.2 示例代码
12 路由懒加载&异步组件
默认情况下打包的所有路由它对应的js和样式都会打包到一个js文件和一个css里,并且在页面一打开时就加载了【这样做的好处:1.切换时非常丝滑;2.大大减少对服务器的请求次数】【这样做的坏处:初次加载慢(首屏加载慢)】
可能有些项目非常注重首屏时间,希望用户越快打开首页越好,就希望不能用这种打包到同一个文件里的行为,所以这时候就要用路由懒加载,也叫异步组件
12.1 路由懒加载语法
将以前的import 组件 from ‘路径’ 改成如下形式就是路由懒加载了
语法 | 描述 |
const组件名= () => import('@/views/组件名.vue ') | 路由规则所在的index.js文件中将导入组件的代码改为这种形式 |
13 Vuex介绍
就是一套集中式的数据管理插件,它能让项目里所有组件都能访问到vuex里的数据【除了父子组件之间的传值外,其他数据可以通过vuex的方式传。】
13.1 用处
方便不同组件的传值【没有vuex之前,要做兄弟组件、或者隔了好多代的组件传值非常麻烦】
13.2 哪些数据适合放到vuex中
如果一个数据多个组件要用,那就放到vuex里
13.3 下载Vuex
命令行使用自定义选择创建项目可以在 创建的时候选择vuex就可以没有现在自己手动下载这一步
语法 | 描述 |
vue add vuex | 项目中打开命令行输入该命令,输入y,下载完成,项目中会多一个@/store/index.js文件,并且有初始化代码 |
13.4 初始化vuex数据【@/store/index.js】
我们只需要在state对象中写我们需要的属性和值,其他的都是脚手架帮我们自动生成了的。
语法 | 描述 |
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { 属性名:’属性值’ }, getters: { }, mutations: { 方法名(state,形参){ /* 参数1:一般写state表示state中的数据,当然也可以自定义。 参数2:调用该方法时传递过来的参数 */ } }, actions: { }, modules: { } }) | state是vuex里放数据的地方 外界组件内的html中通过$store.state.属性名访问vuex中的数据 外界组件内的methods下面的方法中通过this.$store.state.属性名访问vuex的数据 state不允许外界直接被修改【如果直接修改:1.调试工具检测不到改变,不利于调试;2.如果开启严格模式,就会报错】【如何开启严格模式:就是在vuex里跟state平级的地方加strict:true】 只能在mutations里面的方法中可以修改state里面的数据,且这里面的所有方法的第一个参数就是上面的state对象,第二个参数为其他组件使用方法中使用$store.commit(‘通知vuex调用mutations中的方法名’,传递到方法中的参数)传递过来的参数 其他组件使用$store.commit(‘通知vuex调用mutations中的方法名’,传递到方法中的参数)即可在vuex中的mutations中去调用对应的方法 |
13.5 使用vuex中的数据
$store.state.属性名不能直接修改vuex中的数据【直接使用$store.state.属性名=值的方式修改vuex中的数据,界面会更新,但浏览器上的调试工具监测不到变化,不利于调试。直接使用$store.state.属性名=值的方式修改vuex中的数据,如果vuex开启了严格模式,会报错】【vue官网都说了vuex中的数据是不允许直接被修改的,唯一允许修改state的地方,在vuex的mutations中。】
语法 | 描述 |
$store.state.属性名 | 某个组件 在html结构的插值表达式中使用vuex中的数据 |
this.$store.state.属性名 | 某个组件 在methods内的方法中使用vuex中的数据 |
13.6 使用计算数学简化state的访问
语法 | 描述 |
computed: { 自定义访问vuex中数据的计算属性 () { return this.$store.state.vuex中的属性 } } | 通过该方式可以轻松获取vuex中的数据【但是每访问一个vuex中的数据,就需要写一个计算属性】 |
13.7 辅助函数 -mapState【简化计算属性访问vuex中的数据】
Vuex也发现让你写计算属性,你会觉得麻烦,所以给你提供了一个辅助函数叫mapState专门帮你生成上面的计算属性
13.7.1 mapState语法
首先需要导入mapState辅助函数,在计算属性computed中写…mapState([‘vuex中state内的属性1’, ‘vuex中state内的属性2’,…])【调用mapState辅助函数,会将数组中的所有属性都以计算属性的方式返回出来,且返回值为this.$store.state.属性名的方式返回出来。】
语法 | 描述 |
//先导入辅助函数 import {mapState} from ‘vuex’ //计算属性中调用 computed:{ …mapState([vuex中state内的属性列表]) } | 在需要访问vuex中数据的组件中先导入vuex的mapState函数,然后在计算属性中调用该方法。将需要使用计算属性访问vuex中的属性全部写在辅助函数mapState的数组属性列表中。 |
13.8 修改vuex中的数据【vuex中的mutations】
只能在mutations里面方法中修改state里面的数据,且这里面的所有方法的第一个参数就是上面的vuex中的state对象【通过对象的形式保存了vuex中的所有的数据】,第二个参数为其他组件使用方法中使用$store.commit(‘通知vuex调用mutations中的方法’,传递到方法中的参数)代码中的第二个参数
mutations只能额外接收一个参数,如果要传多个,可以传对象。【通过$store.commit(‘通知vuex调用mutations中的方法’,传递到方法中的参数)调用vux中mutations中的方法时,只能传递一个参数过来】
13.8.1 组件中的按钮事件
语法 | 描述 |
<标签名 @事件类型=’目标方法名’></标签名> | 当按钮事件触发时,会去methods中找到对应的方法 |
methods:{ 目标方法名(){ this.$store.commit(‘vuex中mutations中的目标方法’,传递过去的参数) } } | this.$store.commit会在vuex的mutations中去找对应的方法,并将该参数传递过去【注意,只能有一个参数传过去有效】 |
13.8.2 vuex中mutations下面的方法【写同步方法的地方】
语法 | 描述 |
mutations: { // 第一个参数只能是上面的state,可以使用vuex中的数据,第二个参数是其他组件$store.commit('方法名',参数)传递过来的参数 目标方法 (state, p1) { // state.属性名相关的赋值表达式 /* this.state.age = state.age + p1 */ state.age = state.age + p1 } | mutations中的方法,所有第一个参数是state也就是存放的数据,第二个参数为其他组件使用this.$store.commit(‘vuex中mutations中的目标方法’,传递过去的参数)中的第二个参数,且目标方法中只能接收一个数据,想接收多个数据可以使用对象传过来 |
13.9 辅助函数mapMutations
每次在组件里访问vuex里的mutations中的目标方法都要写this.$store.commit(‘目标方法名’,参数)【这样比较麻烦,希望this.方法名就是我们需要的mutions中的方法,所以需要mapMutations辅助函数。】
使用辅助函数首先需要按需导入vux并暴露除mapMutations这个方法。【当组件中的目标方法名和vuex中mutations中的方法名相同时】
13.9.1 组件中的按钮事件【没用辅助函数之前的原始写法】
语法 | 描述 |
<标签名 @事件类型=’目标方法名’></标签名> | 当按钮事件触发时,会去methods中找到对应的方法 |
methods:{ 目标方法名(传递过去的参数){ this.$store.commit(‘vuex中mutations中的目标方法名’,传递过去的参数) } } | this.$store.commit会在vuex的mutations中去找对应的方法,并将该参数传递过去【注意,只能有一个参数传过去有效】【当目标方法名和vuex中mutations中的目标方法名一样的时候,调用组件内部的方法就等同于调用vuex中的方法。】 |
13.9.2 组件内写法【用了辅助函数前后对比】
没有辅助函数前,操作vux中mutions中的方法每次都需要在组件内部使用this.$store.commit(‘vuex中mutations内部的方法名’,传递的参数),当一个组件内部有多个按钮都要操作vuex中的数据时,这样写就比较麻烦,所以有了辅助函数后,一行代码就搞定。
没用辅助函数语法 | 描述 |
<标签名 @事件类型=’目标方法名’></标签名> | 当按钮事件触发时,会去methods中找到对应的方法 |
methods:{ 目标方法名(传递过去的参数){ this.$store.commit(‘vuex中mutations中的目标方法名’,传递过去的参数) } } | this.$store.commit会在vuex的mutations中去找对应的方法,并将该参数传递过去【注意,只能有一个参数传过去有效】【当目标方法名和vuex中mutations中的目标方法名一样的时候,调用组件内部的方法就等同于调用vuex中的方法。】 |
用辅助函数简写语法 | 描述 |
//先导入vuex并暴露mapMutations这个函数 import {mapMutations} from ‘vuex’ methods:{ …mapMutations([vuex中mutations中的方法名列表]) } | 使用辅助函数首先需要导入并暴露辅助函数import {mapMutations} from ‘vuex’ |
…mapMutations([vuex中mutations中的方法名列表]) 就等价于 vuex中mutations中的目标方法名(传递过去的参数){ this.$store.commit(‘vuex中mutations中的目标方法名’,传递过去的参数) } |
13.10 vuex里的actions
它也是放方法的地方,但是它里面可以写异步代码
actions和mutations一样,内部的方法只能额外接收一个参数。
当一个请求数据在多个页面都需要使用时,将请求写在vuex的actions中的方法中【避免每个页面都写一遍这个请求代码】,调用vuex中mutations方法的代码写在actions内方法的最后一行。
语法 | 描述 |
actions:{ 方法名(ctx){ ctx.commit(‘mutation中的方法名’) } } | actions里面的方法第一个参数都是context表示当前store实例,我们简写为ctx【固定写法】异步代码就写在这里面 ctx代表当前store对象【里面有state、commit、dispatch方法】 其他组件的methods方法中使用this.$store.dispatch(‘vuex中actions中的目标方法’,传过去的参数)即可调用vuex中actions里面的目标方法【actions方法里面又可以通过ctx.commit的方式调用mutation里面的方法,从而实现对数据的修改】 |
13.11 辅助函数mapActions
语法 | 描述 |
//先导入vuex并暴露mapActions这个函数 import {mapMutations} from ‘vuex’ methods:{ …mapActions([vuex中actions中的方法名列表]) } | …mapActions([vuex中actions中的方法名列表]) 就等价于 vuex中actions中的方法目标名(传递参数形参){ this.$store.dispatch(vuex中actions中的目标方法名, 传递参数形参) } |
在该组件实例中调用vuex中actions中的方法就可以直接在methods的方法中使用this.vuex中actions中对应的方法名 | |
在其他模块【js文件中】调用vuex中actions中的方法:开启命名空间和分开设置子模块后,可以使用this.$store.dispatch(‘子模块路径/子模块名’,传递的参数) |
13.15 Vuex里的计算属性getters
相当于是vuex里的计算属性
13.15.1 模块中的语法
语法 | 描述 |
getters:{ 计算属性名(state){ return state中属性相关表达式 } } | 当需要使用某个模块中的计算属性时 在组件的html结构的插值表达式内写 $store.getters.vuex中getters中的计算属性名 $store.getters[‘模块名/计算属性名’] |
13.16 mapGetters
简化访问getters的写法因为每次要访问getters里的数据都要写$store.getters.计算属性名
语法 | 描述 |
import {mapgetters} from ‘vuex’ computed:{ …mapGetters([计算属性名列表]) } | 左边代码等价于 getters:{ 计算属性名(state){ return $store.getters[‘模块名/计算属性名’] } } |
13.17 vuex中小结
结论 | 描述 |
1 | Vuex中的state是放数据的地方 |
2 | Vuex中mutations内部的方法中,第一个参数为vuex中保存数据的state对象,第二个参数为传递过来的参数值,且最多只能传递一个参数【mutations放同步方法的地方,也是唯一能够修改state的地方】 |
3 | Vuex中actions内部的方法中,第一个参数为vuex中store对象【里面有commit,dispatch等方法】,第二个参数为传递过来的参数值,且最多只能传递一个参数 |
4 | Vuex中gitters内部的方法中,第一个参数为vuex中保存数据的state对象,第二个参数为传递过来的参数值,且最多只能传递一个参数 |
5 | 辅助函数mapState,mapGetters和属性数据有关,写在组件的computed计算属性内部调用【简化组件中访问vuex内部数据的方法】 |
6 | 辅助函数mapMutations,mapActions和方法有关,写在组件的methods方法内部调用【…mapMutations([‘方法名’])】 |
14 Vuex的模块化
所有的数据,更新,操作都在一起,项目越大,越难维护,把不同的模块拆分不同的js文件里
以后真实开发,一般不会直接全部写道store/index.js里,因为这样随着项目越大,就越难维护。以后的开发都是以模块来区分数据,把不同的模块的vuex数据拆分到不同的js文件里,然后通过modules属性,让vuex跟这些js文件关联到一起
14.1 vuex模块化
Vuex模块化,需要在@/store文件夹下面新建一个modules文件夹【专门用于存放不同模块的vuex数据和方法】,在每个子模块中都需要开启命名空间namespaced:true
步骤 | 文件描述 | 过程 |
1 | 新建modules文件 | 在@/store文件夹下面新建一个modules文件夹【用于存放不同模块的vuex数据】【@/store/modules】 |
2 | 新建vuex模块 | 在modules文件夹下面新建一个A模块【A.js文件】,用于放该A模块中在vuex内的数据 |
3 | 将该模块的vuex数据暴露给@/store/index.js文件使用 | 在A模块中写暴露数据的语法【state需要写函数返回值的形式】 |
export default { state () { return { cartList: [ { id: 1, name: '皮鞋', price: 9.9 }, { id: 2, name: '玩具', price: 10 }, { id: 3, name: '外套', price: 20 } ] } }, mutations: { setCartList (state, list) { state.cartList = list } }, actions: {}, getters: {} } | ||
4 | @/store/index.js文件暴露的对象中添加modules属性 | 来到@/store/index.js文件导入A模块,并在state平级添加modules属性,modules属性对应的属性值为对象,对象中保存了所有导入的vuex模块。 |
5 | @/store/index.js文件中的代码 | // 导入vuex模块【访问子模块之前需要先导入之模块,然后在modules对象属性内部注册之模块】 import 模块名 from '@/store/modules/模块名.js' export default new Vuex.Store({ modules: { // 把vuex模块注册为一个模块 模块名 } }) |
6 | 访问vuex中的数据和方法 | 使用模块中的数据$store.state.模块名.属性名 |
使用方法$store.commit(‘方法名’,传递的参数)【在没有开启命名空间前,所有store文件夹中的模块内相同方法名都会被调用。】 | ||
7 | 开启命名空间并注册模块后访问vux对应模块的数据和方法 | $store.state.属性名【访问根模块中vuex数据】 |
$store.commit(‘方法名’,传递的参数)【访问根模块vuex中mutation内的方法】 | ||
$store.dispatch(‘方法名’)【访问根模块vuex中actions内的方法】 | ||
$store.getters.计算属性名【访问根模块vuex中getters内的计算属性】 | ||
使用目标模块中的state中的数据 $store.state.模块名.属性名 | ||
使用目标模块中的mutations中的方法$store.commit(‘目标模块名/方法名’,传递的参数)【调用@/store/modules/目标模块名文件内部的mutations内部的方法名】 | ||
使用目标模块中的actions中的方法$store.dispatch(‘目标模块名/方法名’,传递的参数)【调用@/store/modules/目标模块名文件内部的mutations内部的方法名】 | ||
使用目标模块中的getters中的计算属性$store.getters[‘目标模块名/方法名’]【访问@/store/modules/目标模块名文件内部的getters内部的计算属性】 | ||
8 | 开启命名空间后,组件内部简化访问vuex对应模块的数据和方法 | …mapState([‘vuex中state中的属性’])这种方式只能简化根vuex中的数据访问,不能简化模块中的数据访问。【导入辅助函数并映射vuex模块中的数据后,可以使用this.vuex根模块中state中的属性简化根模块vuex中state数据的访问】 |
…mapState(‘目标模块名’,[‘vuex中模块中的state中的属性’])这种方式简化模块中的数据访问。【组件内部导入辅助函数并映射vuex模块中的数据后,可以在组件内部使用this.vuex目标模块中state中的属性简化目标模块vuex中state数据的访问】 | ||
…mapMutations([‘vuex中模块中的mutations中的方法’]) 这种方式只能访问根模块中的方法【导入辅助函数并映射vuex模块中的方法后,可以使用this.vuex根模块中mutations中的方法简化根模块vuex中mutations方法的访问】 | ||
…mapMutations(‘目标模块名’,[‘vuex中模块中的mutations中的方法’]) 这种方式简化模块中的方法访问【组件内部导入辅助函数并映射vuex模块中的方法后,可以在组件内部使用组件中使用this.vuex模块mutations中的方法即可访问使用目标模块中vuex中mutations方法的访问】 | ||
$sotre.state.模块名.vuex对应模块中state中的属性 | ||
$store.dispatch(‘模块名/方法名’) |
14.2 辅助函数映射子模块的说明【开启命名空间后】
映射根模块就一个参数
语法 | 描述 |
mapState([‘属性名’]) | 映射根模块中的state中的属性名 |
mapMutations([‘方法名’]) | 映射根模块中的mutations中的方法名 |
mapActions([‘方法名’]) | 映射根模块中的actions中的方法名 |
mapGetters([‘计算属性名’]) | 映射根模块中的getters中的计算属性名 |
映射子模块两个参数。
参数1是模块名;参数2是要映射的列表
语法 | 描述 |
mapState(‘模块名’,[‘属性名’]) | 在该组件内部就可以使用列表属性名的方式直接访问vux对应模块中的属性。 |
mapMutations(‘模块名’, [‘方法名’]) | 在该组件内部就可以使用列表方法名的方式直接访问vux对应模块中的方法。 |
mapActions(‘模块名’, [‘方法名’]) | 在该组件内部就可以使用列表方法名的方式直接访问vux对应模块中的方法。 |
mapGetters(‘模块名’, [‘名字’]) | 在该组件内部就可以使用列表属性名的方式直接访问vux对应模块中的属性了。 |
14.3 vuex模块化后辅助函数简化
每个模块需要开启命名空间,为了统一访问和使用vuex中的数据和方法,我们推荐使用辅助函数完成。【组件内部访问vuex不同模块中的数据和方法】
使用辅助函数后,访问vux模块中的数据,直接使用属性名或计算属性名即可;访问vuex中的方法,直接使用方法名即可
组件中语法 | 描述 |
…mapState(‘模块名’,[‘vuex中该模块中的属性’]) | 等价于在computed选项下面写如下代码 属性名(){ return this.$store.state.模块名.属性名 } |
…mapGetters(‘目标模块名’,[‘vuex中模块中的getters中的计算属性’]) | 等价于在computed选项下面写如下代码 计算属性名(){ return $store.getters[‘模块名/计算属性名’] } |
…mapMutations(‘目标模块名’,[‘vuex中模块中的mutations中的方法’]) | 等价于在methods选项下面写如下代码 方法名(传递的参数){ this.$store.commit(‘目标模块名/方法名’,传递的参数) } |
…mapActions(‘目标模块名’,[‘vuex中模块中的mutations中的方法’]) | 等价于在methods选项下面写如下代码 方法名(传递的参数){ this.$store.dispatch(‘目标模块名/方法名’,传递的参数) } |
14.4 持久化vuex里的数据
vuex的数据跟data中的数据一样的特点:一刷新就恢复默认值。
而token不能让他一刷新就没了,所以也应该持久化存起来。
语法 | 描述 |
import { loginAPI } from '@/api/user' // 1.1 导入操作本地存储的函数 import { getToken, setToken } from '@/utils/storage' export default { namespaced: true, state () { return { token: getToken() } }, mutations: { setToken (state, token) { state.token = token // 1.2设置token到本地存储 setToken(token) } }, actions: { async login (ctx, obj) { const res = await loginAPI(obj) ctx.commit('setToken', res.data.token) } } } | 因为vuex的数据跟data中的数据一样的特点:一刷新页面就恢复默认值。所以如果要让vux中的初始值为token,要么直接赋值【本地存储中的token给到我们vuex中的token中】,要么就需要在进入页面前就调用mutations中的setToken方法【在登陆页面的使用mapMutations映射user模块的setToken方法,然后在登陆页面的created钩子函数中调用user模块的setToken方法,并传入表单数据】 |
15 命名空间
没有命名空间的时候,如果要调用mutations里的方法,会造成调用不明确【你调用mutations的方法,它会把所有模块内vuex中mutations里的同名方法都调用一次。】
没有命名空间的时候,mapState、mapMutations这些辅助函数都无法映射子模块里的数据和方法【只会映射根模块里的数据和方法】【解决方法:开启命名空间】
15.1 创建子模块并开启命名空间
开启命名空间后,我们就可以指定模块执行对应模块mutations内部的方法,或者获取对应的模块数据。
语法 | 描述 |
namespaced:true | 每个暴露的子模块的vuex中使用该行代码创建命名空间 |
开启命名空间,以后就可以通过命名空间指定路径的方式来执行vuex中的模块中的方法 | |
开启命名空间后,所有组件中访问state里的数据还是使用$store.state.模块名.数据名 | |
开了命名空间后调用方法$store.commit(‘模块名/方法名’) |
15.2 store/index.js中导入和注册子模块
语法 | 描述 |
import 模块名 from ‘模块路径’ modules:{ 模块名 } | 在store/index.js中导入对应的模块,然后注册子模块,让子模块和vuex联系起来 modules中为注册的子模块 |
15.3 访问子模块里的state
语法 | 描述 |
$store.state.模块名.数据名 | 访问固定模块的数据。 |
15.4 调用子模块里的mutations
语法 | 描述 |
$store.commit(‘模块名/方法名’,传递的额外参数) | 开启了命名空间才可以指定调用子模块里的方法名【否则只能使用$store.commit(‘方法名’,传递的参数),但这样会将所有子模块中mutations内的该方法都会执行】 |
15.5 访问子模块里的gitters计算数学
语法 | 描述 |
$store.getters[‘模块名/计算属性名’] | 没有mapGitters方法时,写法怪异 |
15.6 辅助函数映射子模块
辅助函数是在组件实例上面用的,用于映射模块【vuex模块js文件】里的vuex属性【state属性和getters属性】和方法【mutations和actions中的方法】
语法 | 描述 |
…mapState(‘模块名’,[‘vuex中该模块中的属性’]) | 等价于 属性名(){ return this.$store.state.模块名.属性名 } |
…mapGetters(‘目标模块名’,[‘vuex中模块中的getters中的计算属性’]) | 等价于 计算属性名(){ return $store.getters[‘模块名/计算属性名’] } |
…mapMutations(‘目标模块名’,[‘vuex中模块中的mutations中的方法’]) | 等价于 方法名(传递的参数){ this.$store.commit(‘目标模块名/方法名’,传递的参数) } |
…mapActions(‘目标模块名’,[‘vuex中模块中的mutations中的方法’]) | 等价于 方法名(传递的参数){ this.$store.dispatch(‘目标模块名/方法名’,传递的参数) } |
16 vuex总结
16.1 四大总结
Vuex四大 | 描述 |
state | 放数据的地方 |
mutations | 放同步方法的地方,也是唯一能够修改state的地方 |
actions | 放异步方法的地方,可以写异步代码 |
以后的流程一般是actions里发请求获取数据,然后调用mutations里的方法进行对state的数据修改 | |
gitters | Vuex里的计算属性 |
16.2 辅助函数
跟数据有关的辅助函数有mapState、mapGetters,都是放在计算属性里调用【…mapState([state中属性1名,state中属性2名,…])】
跟方法有关的辅助函数mapActions、mapMutations,都是放在methods方法里调用【…mapMutations([mutations里面的方法名列表,…])】
语法 | 描述 |
computed:{ …mapState([state中属性1名,state中属性2名]) } | 在组件内的html结构中就可以直接在插值表达式{{}}中使用state中的属性名的方式来获取数据了。 |
16.3 修改vuex中的数据
修改数量不单纯是数字的变化,而是要把改动的结果发请求给服务器,以及还要把本地vuex数据也修改了
16.4
16.5 style标签样式的 scoped属性
Scoped:让样式只能作用于当前页面,只能作用到当前组件里写过的标签 | |
不包括组件内部的标签【就是页面上自动生成的不是我们自己写的标签】,这些需要生效需要在自动生成的标签选择器的前面加一个深度作用选择器。 |
17 持久化vuex里的token存储
token在很多地方都要用,就要存起来,以前我们是存到本地存储,但是这次还不够,还要存到vuex里【因为本地存储的token需要在插值表达式中使用,每次都需要先导入获取token的方法,然后在赋值给data中的数据后放到插值表达式中,极其麻烦】【将token放到vuex中,以后就可以使用this.$store.state.token变量名的形式直接在插值表达式中使用了。而且还可以引入mapState方法来简化我们的书写】
Vuex的数据跟data中的数据一样的特点:一刷新就恢复默认值了【刷新页面就会变为初始值,所以vuex数据的持久化要么是本地存储,要么是修改请求而来的数据】
保持样式风格统一
之前我们在login页写样式时,颜色时写死的;如果以后主题颜色需要更换,那就应该用一个变量来指定这些颜色,这样的好处是:变量的值一变,其他所有地方跟着变
18 组件中less相关
语法 | 描述 |
$变量名=变量值 | 目标文件路径/目标文件名.scss |
@import url(‘目标文件路径/目标文件名.scss’) | 在需要的组件中导入该代码 |
19 组件中scss相关
语法 | 描述 |
$变量名=变量值 | 目标文件路径/目标文件名.scss |
@import ‘目标文件路径/目标文件名.scss’ | 在需要的组件中导入该代码 |
20 深度作用选择器
为了让组件不成为全局样式造成样式冲突,那一定要加scoped,但是加了scoped样式有作用不到组件内部的标签,所以需要用深度作用选择器【主键内部生成的标签选择器需要设置样式,就需要深度作用选择器】
scoped作用:能让样式只作用在当前组件写过的标签上,没写的标签没法作用,也就是无法作用到组件内部的标签
语法 | 描述 |
::v-deep 组件内部生成的选择器{ //相关样式 } | 在scss中使用,::v-deep空格然后再跟上组件内部生成的选择器 |
/deep/ | 在less中使用 |
>>> | 在less中使用 |
服务器优化
20.1 优化思想:
图片与代码【网址内容】进行分离
代码就放在代码服务器
图片就放在图片服务器
20.2 好处:
内容分离,更好管理
万一服务器出问题了,不会影响全部
减轻服务器的压力,因为如果东西都放在同一个服务器,那么这个服务器压力会比较大。
20.3 缺点
如果给图片专门给个服务器,会增大成本
大型项目必须考虑性能问题,所以用成本换性能
20.4 做一个图片服务器比较麻烦,现实开发怎么做
做法 | 优点 |
不怕麻烦,就是自己搭建一个图片服务器提供好服务 | 图片始终保持在自己服务器上,操作权限更大,图片没有泄露风险 |
使用别人提供好的图片服务器 | 方便快捷 |
21 json-server工具
21.1 Json-server工具的使用
restful规范:如果操作同一个数据接口地址都是一样的【接口规定:查询信息用get;新增信息用post;修改信息用patch;删除信息用delete】【前端写测试接口遵循这个规范,后端写正式接口也遵循这个规范】
准备一个json文件,把db文件夹复制到项目根目录,小黑窗打开这个db目录,输入json-server 文件名.json即可生成在线接口【开了就不要关,关了就表示在线接口没了。】。【因为遵循restful规范,所以get请求这个地址就是获取,post请求这个地址就是新增,patch请求这个地址就是修改,delete请求这个地址就是删除】
步骤 | 过程 |
1 | 安装json-server-g工具:项目文件夹打开小黑窗输入 npm i json-server -g |
2 | 准备一个.json后缀的文件【用于接口数据】。 |
3 | 项目根目录放一个db数据库文件,json文件放在里面【方便查找接口数据】 |
4 | 存放.json文件这个文件夹打开小黑窗,输入json-server 文件名.json【得到在线接口地址】 |
5 | 测试接口就可以使用生成的网址来测试【在后端的接口没写好的情况下。】 |
21.1.1 调用接口案例
接口方法 | 语法 |
修改 | axios({ url:`在线网址/${数据唯一标识}`, method:’patch’, data:{ 修改属性:修改后的属性值 } }) |
新增 | axios({ url:`在线网址`, method:’post’, data:{ 新增属性:新增属性值 } }) |
查询 | axios({ url: `在线网址/${数据唯一标识}`, }) |
22 复习find方法
数组名为arr,数组中保存的数据类型为对象复杂数据类型,
语法 | 描述 |
const item = arr.find(v=>v.属性===属性值) | item得到的是一个查询到的目标元素,对元素进行修改操作会改变原数组对应那个元素的值【因为是复杂数据类型】【所以我们需要改变数组中元素单个数据时可以使用该方法】 |
第四章
1 移动端项目搭建
1.1 创建项目
步骤 | 过程 |
1 | 创建一个项目文件夹 |
2 | 进入项目文件夹并打开小黑窗cmd |
3 | 小黑窗输入vue create 项目名 |
4 | 按键盘上下键 选择 Manually select features |
5 | 按键盘上下键 空格选中【选项前面的括号内有星号*表示被选中状态】 [Babel【es6->es5 按需导入】,Router【路由】,CSS Pre-processors【CSS预处理】,Linter/Formater【eslint-语法检查】]当数组中的所有插件都被选中后,按回车【空格可以选中和取消】 |
6 | 上下键选择2.x【表示使用vue2.x版本】,按回车 |
7 | Use history mode for router?后面输入n【表示不使用历史模式,就会使用hash模式】,按回车 |
8 | 选择Less【css预处理插件】, 按回车 |
9 | 上下键选择 ESLint +Standard config【表示使用ESLint语法检查的标准模式,这样可以规范我们的代码】 |
10 | 选中Lint on save【在保存时校验语法是否规范,选这个】 |
11 | 选择In dedicated config files【你在哪放babel、eslint这些插件的配置,用独立的文件做它们的配置,所以建议选第一个,方便后面修改配置文件。】 |
12 | Save this as a preset for future projects?我们输入n【表示不保存,不需要用于下一次创建直接使用】【如果输入了y,后续不想要有这个直接使用项,需要在C:\Users\自己用户名\.vuerc去修改,将presets里面的对应名字配置删掉即可】 |
1.2 下载vue2的vant
语法 | 描述 |
npm i vant@latest-v2 -S | 在需要使用vant的移动端项目使用该命令下载,也可以使用npm i vant@latest-v2 -S -f下载哪种下载报错版本太高的问题 |
1.3 安装babel-plugin-import 插件
它会在编译过程中将 import 的写法自动转换为按需引入的方式。
语法 | 描述 |
npm i babel-plugin-import -D | 如果报错可以使用npm i babel-plugin-import -D -f来下载这个插件 |
1.4 在项目根目录【与package.json平级】配置babel.config.js文件】
语法 | 描述 |
module.exports = { presets: ['@vue/cli-plugin-babel/preset'], plugins: [ [ 'import', { libraryName: 'vant', libraryDirectory: 'es', style: true }, 'vant' ] ] } | 这是配置babel-plugin-import这个插件,以后保存就会自动转为按需引入的语法了 |
1.5 在src源代码目录下新建两个文件夹
api文件夹与utils文件夹【下面是文件夹路径】
文件夹路径 | 描述 |
项目名/src/utils | 该文件夹叫工具文件夹,放一些自己封装的函数和第三方插件的导入【因为项目文件夹名/src/main.js是项目的入口文件,我们在这里按需导入我们需要的模块,但是如果导入一个模块后要使用该模块,如果使用该模块的代码有点多,我们就可以用新的js文件来写该模块的使用代码。】 |
项目名/src/api | 该文件夹叫接口文件夹,以后在这里封装一些发送请求调用接口的代码,好处:以后万一这个接口多个项目要用,直接复制api文件夹到别的项目就可以了 |
1.6 删除项目中不需要的文件和代码
不需要的文件夹和文件 | 描述 |
项目名/src/components/HelloWord.vue | 需要删除的文件 |
项目名/src/views/AboutView.vue | |
项目名/src/views/HomeView.vue | |
项目名/src/assets/logo.png | |
不需要的文件中的代码 | 描述 |
项目名/src/App.vue | 删除#app容器中的所有html结构 |
项目名/src/router/index.js | 删除导入的HelloWord.vue和HomeView.vue的代码 删除路由规则中的规则 |
1.7 下载项目中用vm进行屏幕适配
1.7.1 下载把px转成vw/vh的插件
语法 | 描述 |
npm install postcss-px-to-viewport --save-dev --force | 进入项目文件夹并打开小黑窗,输入该命令可以下载该插件 |
1.7.2 提供该插件的配置
来到项目根目录【跟package.json平级】的位置新建postcss.config.js,添加下面配置
语法 | 描述 |
module.exports = { plugins: { 'postcss-px-to-viewport': { viewportWidth: 375 } } } | 该配置可以在浏览器中看到以这个视口宽度为参考并转为vw单位后的值。 |
1.8 下载发送请求需要的axios插件
语法 | 描述 |
npm install axios -f | 命令行下载axios,发送请求需要使用 |
1.9 在工具包utils文件夹中新建4个.js文件
多个页面需要使用的代码就可以放在utils工具包文件夹中
文件名 | 描述 |
vant.js | 移动端布局的每个页面都需要使用vant,我们将它写在main.js入口文件中,但为了减小代码体积,我们使用vant时需要按需导入,但这样又会让main,js中的代码增多,为了便于维护,我们将使用按需导入的vant的代码封装在一个js文件中,然后再将写好的代码导入到main.js【import ‘vant路径’】入口文件内,让main.js文件看着清爽。 |
localStorage.js | 很多页面都有一个身份验证的携带token【变量名可能是token,也可能是其他变量名,如果给他写在每个页面,当token变量名改变时,需要在每个页面都修改token变量名,不方便维护,所以为了便于维护,我们封装一个localStorage.js文件,统一携带token,让token的值为一个常量,后续只需要修改这一处值即可实现所有页面的token变量一起改变。】 |
request.js | 很多页面都需要使用axios,而且可能有多个接口,当使用多个接口时,我们需要不同的基地址,所以,可以在这个文件中封装多个不同的类axios对象【该对象已经有了基地址了】 语法:1. import axios from 'axios' 2. const request = axios.create({ baseURL: '基地址', timeout: 超时响应404的时间毫秒数 }) 3. // 暴露出去其他页面要用 export default request 其他页面导入该js文件后需要使用直接用语法如下 import { request } from '@/utils/request.js' request({ method:’请求方法’ }) 这里还可以对request添加请求拦截和响应拦截 |
1.10 创建路由组件
1.11 在router/index.js文件中导入需要的路由文件并创建路由规则。
语法 | 描述 |
import 组件名 from '@/views/路由组件名' const routes = [ { path: '/资源路径', name: '路由1名', component: 组件1名, children: [ { path: '子路由路径', name: '子路由名字', component: 组件名 } ] }, { path: '/资源路径2', name: '路由2名', component: 组件2名 } ] | 当某个组件需要当路由使用时,需要在router/index.js中导入该组件,然后写路由规则。 |
1.12 在main.js中导入vant.js
语法 | 描述 |
import Vue from 'vue' import App from './App.vue' import router from './router' import '@/utils/vant.js' |
1.13 组件中需要vant哪个部分,就在官方文档中按需导入
1.13.1 按需导入对应的模块并安装使用【@/utils/vant.js】
语法 | 描述 |
import Vue from 'vue' import { Form, Field, Button } from 'vant' Vue.use(Form) Vue.use(Field) Vue.use(Button) | 将需要的模块导入使用。 |
1.13.2 在路由组件中导入vant官网中的组件结构代码和js代码。
1.14 在需要使用utils文件夹类功能模块文件的组件中导入对应的功能模块文件,并导出需要使用的变量
语法 | 描述 |
import { 暴露变量1, 暴露变量2,… } from '@/utils/功能模块文件名.js' | 将需要执行和导出的变量在该组件的js代码中写一次。 |
1.15 将路由组件里面的结构用新的子组件保存。
子组件内部需要使用的数据放在路由组件中,这样可以防止子组件数据写死的情况
1.16 下载vuex
当有的数据多个页面都需要使用时,可以将该数据放在vuex中
语法 | 描述 |
vue add vuex | 进入项目文件夹并打开小黑窗输入该代码 在生成的store/index.js文件中的state中写我们多个页面需要的数据 其他组件使用时通过$store.state.属性名的方式访问 |
1.17 准备后端接口json-server工具【如果需要测试接口】
准备一个json文件,把json文件放在db文件夹内,db文件夹放到项目根目录
语法 | 描述 |
npm i json-server -g | 进入项目文件夹并打开小黑窗,输入该命令 |
它遵顼restful规范【1.gei请求这个地址就是获取;2.post请求这个地址就是新值;3.patch请求这个地址就是修改;4.delete请求这个地址就是删除】
开启后不要关了,关了就代表接口无了
1.18 配置相对路径【如果就想使用绝对路径可以不配置】
脚手架打包时默认生成的路径是绝对路径,里的/,代表当前环境的根目录,因为脚手架的作者默认认为你的网站就是放在服务器根目录上的
语法 | 描述 |
const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, // 设置打包build时生成相对路径的html,css,js文件 publicPath: '' }) | 打开配置文件vue.config.js,将注释行下面的publicPath:’’这行代码写入配置文件中,然后关闭vscode重新打包 |
1.19 打包发布
项目写完了需要压缩代码,把文件转为浏览器认识的html、css、js【但这些不用我们自己做,脚手架已经提供了,我们只要运行命令】
语法 | 描述 |
npm run build | 以后不一定叫build,具体要看package.json,一般都是build或者build:prod |
2 pc端项目搭建
2.1 创建项目
步骤 | 过程 |
1 | 创建一个项目文件夹 |
2 | 进入项目文件夹并打开小黑窗cmd |
3 | 小黑窗输入vue create 项目名 |
4 | 按键盘上下键 选择 Manually select features |
5 | 按键盘上下键 空格选中【选项前面的括号内有星号*表示被选中状态】 [Babel【es6->es5 按需导入】,Router【路由】,Vuex,CSS Pre-processors【CSS预处理】,Linter/Formater【eslint-语法检查】]当数组中的所有插件都被选中后,按回车【空格可以选中和取消】 |
6 | 上下键选择2.x【表示使用vue2.x版本】,按回车 |
7 | Use history mode for router?后面输入n【表示不使用历史模式,就会使用hash模式】,按回车 |
8 | 选择Sass/Scss【Scss预处理插件】【我们选sass,因为element-ui内部用的就是sass语法】, 按回车 |
9 | 上下键选择 ESLint +Standard config【表示使用ESLint语法检查的标准模式,这样可以规范我们的代码】 |
10 | 选中Lint on save【在保存时校验语法是否规范,选这个】 |
11 | 选择In dedicated config files【你在哪放babel、eslint这些插件的配置,用独立的文件做它们的皮质,所以建议选第一个即这个。】 |
12 | Save this as a preset for future projects?我们输入n【表示不保存,不需要用于下一次创建直接使用】【如果输入了y,后续不想要有这个直接使用项,需要在C:\Users\自己用户名\.vuerc去修改】 |
2.2 删除不需要的文件和文件中的代码
不需要的文件夹和文件 | 描述 |
项目名/src/components/HelloWord.vue | 需要删除的文件 |
项目名/src/views/AboutView.vue | |
项目名/src/views/HomeView.vue | |
项目名/src/assets/logo.png | |
不需要的文件中的代码 | 描述 |
项目名/src/App.vue | 删除#app容器中的所有html结构 |
项目名/src/router/index.js | 删除导入的HelloWord.vue和HomeView.vue的代码 删除路由规则中的规则 |
2.3 下载vue2的 element-ui
语法 | 描述 |
npm i element-ui -S | 下载element ui组件库 |
2.4 安装按需导入的插件
语法 | 描述 |
npm install babel-plugin-component -D -f | babel-plugin-component是按需到引入的插件 |
2.5 修改babel.config.js配置文件
语法 | 描述 |
module.exports = { presets: ['@vue/cli-plugin-babel/preset'], plugins: [ [ 'component', { libraryName: 'element-ui', styleLibraryName: 'theme-chalk' } ] ] } | 修改配置文件后需要重新打开编辑器并重新开启项目 |
2.6 在工具包utils文件夹中新建4个.js文件
文件名 | 描述 |
element.js | pc端布局的每个页面都需要使用element-ui,我们将它写在main.js入口文件中,但为了减小代码体积,我们使用element时需要按需导入,但这样又会让main.js中的代码增多,为了便于维护,我们将使用按需导入的element.js的代码封装在一个js文件中,然后再将写好的代码导入到main.js【import ‘element.js’】入口文件内,让main.js文件看着清爽。 |
localStorage.js | 很多页面都有一个身份验证的携带token【变量名可能是token,也可能是其他变量名,如果给他写在每个页面,当token变量名改变时,需要在每个页面都修改token变量名,不方便维护,所以为了便于维护,我们封装一个localStorage.js文件,统一携带token,让token的值为一个常量,后续只需要修改这一处值即可实现所有页面的token变量一起改变。】 |
request.js | 很多页面都需要使用axios,而且可能有多个接口,当使用多个接口时,我们需要不同的基地址,所以,可以在这个文件中封装多个不同的类axios对象【该对象已经有了基地址了】 语法:1. import axios from 'axios' 2. const request = axios.create({ baseURL: '基地址', timeout: 超时响应404的时间毫秒数 }) 3. // 暴露出去其他页面要用 export default request 其他页面导入该js文件后需要使用直接用语法如下 import { request } from '@/utils/request.js' request({ method:’请求方法’ }) 这里还可以对request添加请求拦截和响应拦截 |
2.7 新建src/utils/element.js文件
该文件用于按需导入element官方文档中需要的组件。
2.8 将src/utils/element.js文件在main.js中执行一下
语法 | 描述 |
import '@/utils/element' | 将elementjs中的代码导入到main.js入口文件中 |
2.9 下载axios发请求的依赖库
语法 | 描述 |
npm install axios -f | 项目需要发请求,所以需要下载axios库 |
2.10 新建src/utils/request.js【封装有基地址的axios实例】
所有基地址的axios实例写在这个文件里面,并将这些实例暴露出去
语法 | 描述 |
/* 封装axios用于发送请求 */ // 超过该时间返回404 | // 添加请求拦截器 |
2.11 新建src/utils/storage.js文件【用于保存本地token】
语法 | 描述 |
const KEY = 'my-token-element-pc' | 当某个组件需要token验证时,就需要先导入该文件并按需导出里面的变量和方法。 // 但是按需导出,导入时必须 import { getToken } from '模块名导入' |
文件夹形式管理页面组件的思想
每个页面都是一个文件夹,里面都有各自index.vue【原因:1.利用文件夹的形式管理每个页面能够让项目更加利于管理;2.以后每个页面里可能又会有多个子页面,或者这个页面里独享的组件】【为什么有index.vue就代表这个页面的主页面】
文件名 | 描述 |
第五章
1 小结
1.一级路由用于单个页面随着路径跳转 |
2.二级路由是更新一级路由那个页面中的局部代码 |
3.组件用于复用,可以简化路由组件的代码,后续需要修改哪部分,直接到对应的组件中修改即可。 |
2 前端和后端、前态项目和后台项目
2.1 前端和后端
开发方向不一样
2.1.1 前端
和界面打交道
大前端的概念:任意界面都属于前端开发
2.1.2 后端
和服务器【接口数据】打交道
2.2 前台和后台
项目方向和区别
前台界面是给所有访问者都能看的,后台界面是给这个网站管理者看的【可以修改数据以及用户权限。】
3 sass/scss【以前叫sass现在叫scss】
3.1 语法
语法和less基本一样
不同点:less声明变量使用@;scss声明变量用$
标签:7vue,js,语法,组件,vuex,路由,属性 From: https://www.cnblogs.com/BlueGirl/p/17023324.html