Vue2 开发笔记
Vue2
一.简介及配置
1.简介
1.1什么是vue
- 构建用户界面
- 用vue往html页面中填充数据,非常的方便
- 框架
- 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能
- 要学习vue,就是在学习vue框架中规定的语法!
- vue的指令、组件(是对UI结构的复用)、路由、Vuex、vue 组件库
1.2vue 的特性
- 数据驱动视图
- 数据的变化会驱动视图自动更新
- 好处:程序员只管把数据维护好,那么页面结构会被vue自动渲染出来
- 双向数据绑定
在网页中,from表单负责采集数据,Ajax负责提交数据
- js数据的变化,会被自动渲染到页面上
- 页面上采集的数据发生变化的时候,会被vue自动获取到,并更新到js数据中
注意:数据驱动视图和双向数据绑定的底层原理是MVVM
1.3MVVM
MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。MVVM指的是 Model、View和ViewModel,它把每个HTML页面都拆分成这三个部分。
- Model 表示当前页面渲染时所依赖的数据源。
- View 表示当前页面所渲染的 DOM 结构。
- ViewModel 表示 vue 的实例,它是 MVVM 的核心。
1.4MVVM的工作原理
2.配置
2.1基本使用步骤
- 导入vue.js 的script脚本文件
- 在页面中声明一个将要被 vue 所控制的 DOM 区域
- 创建 vm 实例对象( vue 实例对象)
<body>
<!-- 希望vue能够控制这个div -->
<div id="app">{{ username }}</div>
<!-- 1.导入 Vue 的库文件 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建 vue 的实例对象 -->
<script>
const vm = new Vue({
// el 属性是固定写法,表示当前vm实例要控制页面上的哪个区域,接受的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
username: 'zs',
}
});
</script>
</body>
2.2基本代码与MVVM的对应关系
2.3浏览器配置
Chrome 浏览器下载 Vue.js devtools
扩展程序
二.语法
1. vue 的指令
1.指令的概念
指令 是 vue 为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构。
vue 中的指令按照不同的用途可以分为如下6大类:
- 内容渲染指令
- 属性绑定指令
- 事件绑定指令
- 双向绑定指令
- 条件渲染指令
- 列表渲染指令
1.1内容渲染指令
内容渲染指令用来辅助开发者渲染DOM元素的文本内容。常用的内容指令有如下3个:
-
v - text
缺点:覆盖元素原有的默认内容
<p v-text='username'></p> <p v-text="gender">性别:</p>
-
{{}}
vue提供的 {{}} 语法,专门用来解决 v-text 会覆盖默认文本内容的问题。这种语法的专业名称是 插值表达式
[ 可书写js语句 ]
<p>姓名:{{username}}</p> <p>性别:{{gender}}</p> -------- <div>1+2的结果是:{{1+2}}</div> <div>{{tips}} 反转的结果是:{{tips.split('').reverse().join('')}}</div>
-
v - html
v - text 指令和插值表达式只能渲染纯文本内容。如果要把包含HTML标签的字符串渲染为页面的HTML元素,则需要使用到 v - html 这个指令:
<div v-html="info"></div> --------- data: { info: '<h1>123</h1>' }
1.2属性绑定指令
注意:插值表达式只能用在元素的 内容节点 中,不能用在 属性节点 中
-
在 vue 中可以使用
v-bind:
指令,为元素的属性动态绑定值; -
简写是英文的
:
-
[ 书写js语句 ] 在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如
<div :title="'box-'+index">这是一个div</div>
<input type="text" v-bind:placeholder="tips">
<hr>
<!-- 简写 -->
<img :src="photo" alt="">
-----------
1.3事件绑定指令
- vue 提供了 v-on 事件绑定指令,用来辅助程序员为 DOM 元素添加事件,简写
@
。 - 语法格式如下:
<!-- 在绑定事件处理函数的时候可以使用小括号传递参数 -->
<!-- v-on : 可以被简写成 @ -->
<button v-on:click="add(2)">+2</button>
<button @click="sub">-1</button>
-------------
// methods 的作用就是定义事件处理函数
methods: {
// 方法名简写
add(n) {
// this === vm
// vm.count += 1;
this.count += n;
},
sub() {
this.count -= 1;
}
}
注意:原生DOM对象有 onclick、oninput、onkeyup等原生事件,替换为 vue 的事件绑定后,分别为 : v-on:click、v-on:input、v-on:keyup
$event 事件对象:
<!-- vue 提供内置变量,名字叫做 $event ,它就是原生DOM的事件对象 -->
<button @click="add(1,$event)">+N</button>
---------
$event 用于自定义事件(子 -> 父)
<Counter :num="item.goods_count" @add="getNewNum(item,$event)"></Counter>
<script>
...
getNewNum(item,e) {
item.goods_count=e;
}
</script>
-------------------------
<script>
methods: {
add() {
this.$emit('add',this.num++)
}
},
</script>
事件修饰符:
在事件处理函数中调用event.preventDefault()或event.stopPropagation()是非常常见的需求。因此,vue提供了事件修饰符的该概念,用来辅助程序员更方便的对事件的触发进行控制。常用的有:
事件修饰符 | 说明 |
---|---|
.prevent | 阻止默认行为(例如:阻止a链接的跳转、阻止表单的提交等) |
.stop | 阻止事件冒泡 |
.capture | 以捕获模式触发当前的事件处理函数 |
.once | 绑定的事件只触发1次 |
.self | 只有在event.target是当前元素对自身时触发事件处理函数 |
<!-- 事件修饰符 -->
<a href="http://www.baidu.com" @click.prevent="show">跳转到百度</a>
案件修饰符
在监听键盘事件时,我们经常需要判断详细的按键。此时,可以为键盘相关的事件添加按键修饰符,例如:
<!-- 按下esc键清空 -->
<!-- 按下enter触发Ajax -->
<input @keyup.esc="cleaeInput" @keyup.enter="commitAjax" type="text">
--------------
methods: {cleaeInput(e) {}, commitAjax(e) {}}
1.4双向绑定命令
vue 提供了 v-model 双向数据绑定指令,用来辅助开发者在不操作 DOM 的前提下,快速获取表单的数据。(只能用在表单元素上)
-
input 输入框
- type="radio"
- type="checkbox"
- type="xxxx"
-
textarea
-
select
<p>用户的名字是:{{username}}</p> <input type="text" v-model="username"> <hr> <select v-model="city"> <option value="">请选择城市</option> <option value="1">广州</option> <option value="2">珠海</option> <option value="3">大理</option> </select> ------------ data: { username: 'zs', city: 3, }
v-model 修饰符
修饰符 | 作用 | 实例 |
---|---|---|
.number | 自动将用户的输入值转为数值类型 | <input v-model.number="age" /> |
.trim | 自动过滤用户输入的首尾空白字符串 | <input v-model.trim="msg" /> |
.lazy | 在”change"时而非“input”实时更新 | <input v-model.lazy="msg" /> |
1.5条件渲染指令
条件渲染指令用来辅助开发者按需控制DOM的显示与隐藏。有两个,分别是:
-
v-if (每次动态创建或移除元素,实现元素的显示和隐藏)
<p v-if="flag">这是被 v-if 控制的元素</p>
- 如果刚进入页面时,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好
-
v-show (动态的为元素添加或移除
display: none
样式,来实现元素的显示和隐藏)<p v-show="flag">这是被 v-show 控制的元素</p>
- 如果需要频繁的切换元素的显示状态,用 v-show 性能更好
在实际开发中,绝大多数情况,不用考虑性能问题,直接使用 v-if 就好了!!!
-
v-else
<p v-if="flag">这是被 v-if 控制的元素</p> <p v-else>这是被 v-else 控制的元素</p>
注意:v-else 指令必选配合 v-if 指令一起使用,否则它将不会被识别
-
v-else-if
<p v-if="type === 'A'">优秀</p> <p v-else-if="type === 'B'">良好</p> <p v-else-if="type === 'C'">一般</p> <p v-else>差</p>
注意:v-else-if 指令必选配合 v-if 指令一起使用,否则它将不会被识别
1.6列表渲染指令
vue提供了v-for列表指令,用来辅助开发者基于一个数组来循环渲染一个列表结果。v-for指令需要使用 arr in arrs 形式的特殊语法,其中:
- arrs 是待循环的数组
- arr 是被循环的每一项
v-for 中的索引
v-for 指令还支持一个可选的第二个参数,即当前项的索引。语法格式为**(arr,index)in arrs**
<tr v-for="(arr,index) in list" :key="arr.id">
<td>{{index}}</td>
<td>{{arr.id}}</td>
<td>{{arr.name}}</td>
</tr>
---
list: [{
id: 1,
name: '张三'
}, {
id: 2,
name: '李四'
}, {
id: 3,
name: '王五'
}]
注意:v-for 指令中的arr项和index索引都是行参,可以根据需要进行重命名。
<!-- 官方建议:只要用到了v-for 指令,那么一定要绑定一个:key属性 -->
<!-- 而且,尽量把id 作为key 的值 -->
<!-- 官方对key的值的类型,是有要求的:字符串或数字类型 -->
<tr v-for="(arr,index) in list" :key="arr.id" :title="arr.name + index">
key的注意事项
- key 的值只能是字符串或数字类型
- key 的值必须具有唯一性(即: key 的值不能重复)
- 建议把数据项 id 属性的值作为 key 的值(因为 id 属性具有唯一性)
- 使用 index 的值当作 key 的值没有任何意义(因为 index 的值不具有唯一性)
- 建议使用 v-for 指令时一定要指定 key 的值(既提升性能,又能防止列表状态紊乱)
1.7 自定义指令
-
私有自定义指令
-
在每个 vue 组件中,可以在 directives 节点下声明私有自定义指令 。
- bind: 只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作。
- inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
- update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的值来忽略不必要的模板更新。
- componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
- unbind: 只调用一次, 指令与元素解绑时调用。
使用bind 函数
<h1 v-color>App 根组件</h1> directives: { color: { // 当指令 第一次 被绑定到元素上的时候,会立即触发 bind 函数 // 为绑定到的 HTML 元素设置红色的文字 bind(el) { // el 表示当前指令所绑定到的那个 DOM 对象 el.style.color = 'red' } } }
使用 binding.value 获取指令绑定的值
<h1 v-color="color">App 根组件</h1> <p v-color="'red'">测试</p> data() { return { color:'blue' } }, bind(el,binding) { // el 表示当前指令所绑定到的那个 DOM 对象 el.style.color = binding.value }
注意:bind 函数只调用 1 次,当 DOM 更新时 bind 函数不会被触发。
updata 函数会在每次 DOM 更新时被调用
directives: { color: { // 当指令 第一次 被绑定到元素上的时候,会立即触发 bind 函数 // 为绑定到的 HTML 元素设置红色的文字 bind(el,binding) { // el 表示当前指令所绑定到的那个 DOM 对象 el.style.color = binding.value }, update(el,binding) { // el 表示当前指令所绑定到的那个 DOM 对象 el.style.color = binding.value } } }
-
函数简写
如果 bind 和 update 函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式:
directives: { color(el,binding) { el.style.color = binding.value } }
-
-
全局自定义指令
-
全局共享的自定义指令需要在 main.js 通过 “Vue.directive()” 进行声明
// 简写 Vue.directive('color',function(el,binding) { el.style.color = binding.value }) Vue.directive('color',{ bind(el,binding) { el.style.color = binding.value }, update(el,binding) { el.style.color = binding.value } })
-
2.过滤器(Vue3取消)
作用:过滤器(Filters)是vue为开发者提供的功能,常用于文本的格式化。本质是一个函数。
- 用在插值表达式和v-bind属性绑定
- 应该被添加到 JavaScript 表达式的尾部,由“管道符”进行调用
<!-- 在双花括号中通过“管道符”调用 capitalize 过滤器,对 message 的值进行格式化 -->
<p>
{{message | capitalize}}
</p>
<div v-bind:id="rawId | formatId">
</div>
-
私有过滤器
-
在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前所控制的 el 区域内使用。
-
//过滤器函数必选定义到filters节点下(私有过滤器,不共享) filters: { // 过滤器函数形参中的 val 永远都是 管道符 前面的那个值 cati(val) { const first = val.charAt(0).toUpperCase(); const other = val.slice(1); //过滤器中一定要有个返回值 return first + other; }
-
-
全局过滤器
-
如果希望在多个vue实例之间共享过滤器,则可以按照如下的格式定义全局过滤器
-
//Vue.filter() 方法接受两个参数 // 参1:是全局过滤器的“名字” // 参2:是全局过滤器的“处理函数” Vue.filter('capitalize',(str) => { return str.charAt(0).toUpperCase() + str.slice(1) + '--' }) //须定义到Vue实例之前
-
-
过滤器的传参
-
过滤器的本质是 JavaScript函数,因此可以接受参数
-
<p> {{message | capitalize(arg1,arg2)}} </p> Vue.filter('capitalize',(msg,arg1,arg2) => { ... })
-
-
注意点:
- 要定义到 filters 节点下,本质就是一个函数。
- 在过滤器函数中,一定要有 return 值。
- 在过滤器的形参中,就可以获取到“管道符”前面待处理的那个值。
- 如果全局过滤器和私有过滤器名字一致,此时就按照“就近原则”,调用的是私有过滤器
- 过滤器可连续调用过滤器
3.侦听器
3.1watch 侦听器
- watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作
const vm = new Vue({
el: '#app',
data: { username: ''},
//所有的侦听器都应该放在watch节点下
watch: {
//侦听器本质上是一个函数,要侦听哪个数据的变化,就把数据名作为方法名即可
//新值在前,旧值在后
username(newVal, oldVal) {
console.log(newVal)
}
//------------------------------------------------------
//对象格式
//定义对象格式的侦听器
username:{
//侦听器的处理函数
handler(newVal,oldVal) {
console.log(newVal,oldVal);
},
//immediate 控制侦听器是否会自动触发
//ture 进入页面时自动触发 false 默认值
immediate: true
}
}
})
-
可用于
-
使用 watch 检测用户名是否可用
watch : { //监听 username 值的变化 async username(newVal) { if(newVal === '') return; //使用 axios 发起请求,判断用户名是否可用 const { data: res} = await axios.get('https://www.escook.cn/api/finduser/'+newVal); console.log(res) } }
-
3.2侦听器的格式
-
方法格式的侦听器
- 缺点1:无法在刚进入页面的时候,自动触发!!!
- 缺点2:如果侦听的是一个对象,对象中的属性发送变化,不会触发侦听器!!!
-
对象格式的侦听器
-
好处1:可以通过 immediate 选项,让侦听器自动触发!!!(默认false)
-
好处2:可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!!!(默认false)
info: { handler(newVal) { console.log(newVal); }, //开启深度监听,只要对象中任何属性变化了,就会触发“对象的监听器” deep: true } //------------------ //监听对象单个属性的变化 //如果要侦听的是对象的子属性的变化,则必选包裹一层单引号 'info.username' (newVal) { console.log(newVal); }
-
优先考虑最简单的用法,如果方法格式够用就不必使用对象格式
4.计算属性
计算属性指的是通过一系列运算之后,最终得到一个属性值。
这个动态计算出来的属性值可以被模板结构或 methods 方法使用。
var vm = new Vue({
el: '#app',
data: {
r: 0, g: 0, b: 0
},
computed: {
rgb() {
return `reb(${this.r},${this.g},${this.b})`
}
},
methods: {
show() {
console.log(this.rgb)
}
}
})
// -------------------------------
<button @click="show">按钮</button>
<div class="box" :style="{ backgroundColor: rgb}">
{{ rgb}}
</div>
- 注意:
- 所有的计算属性。都要定义到computed节点之下,要定义成“方法格式”,要 return 结果
- 在使用时当作普通属性使用
- 在 template 模板结构中可以使用 —— {{}} v-bind
- 在 methods 方法中,也可以使用 —— this.计算属性的名称
- 好处
- 实现代码的复用
- 只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值
三.Vue - CLI
-
扩展
Single Page Application 简称 SPA, 顾名思义,指的是一个Web网站中只要唯一的一个HTML页面,所有的功能都是基于唯一的一个页面内完成的
vue - cli 是 Vue.js 开发的标准工具。简化了程序员基于webpack 创建工程化的 Vue 项目的过程
中文官网:https://cli.vuejs.org/zh/
1.安装和使用
vue - cli 是 npm 上的一个全局包
-
npm install -g @vue/cli
安装 -
vue -V
检测当前版本 -
vue create 项目名称
快速生成工程化的Vue项目优先选择自定义项目设置
-
- 项1:选择 Vue 版本【空格选中】
- 项2:Babel 解决兼容性问题,自动配置【空格选中】
- 项3:微软的脚本语言TS
- 项4:渐进式的 Web 框架
- 项5:路由
- 项6:Vuex
- 项7:CSS 预处理器(如Less)【空格选中】
- 项8:约束团队的代码风格【可暂不选】,代码风格错误则不可允许项目
- 项9-10:单元测试,对组件等测试
-
询问如 Babel,ESLint,etc等插件的配置文件是放在独立文件,还是和package.json中一起?
-
是否将此次设置存为预设?
-
使用 pnpm 还是 npm
-
处理过程中用 ctrl + c 解冻
cd demo-first
切换到项目根目录npm run serve
运行项目
2.vue - cli 项目中目录的构成
- src - assets
- 文件夹,存放项目中用到的静态资源文件,例如:CSS 样式表、图片资源
- src - components
- 文件夹,封装的、可复用的组件,都要放到 components 目录下
- src - main.js
- 项目的入口文件。整个项目的运行,要先执行 main.js
- src - App.vue
- 项目的根组件
3. vue 项目的运行流程
通过 main.js 把 App.vue 渲染到 index.html 的指定区域中
- App.vue 用来编写待渲染的模板结构
- index.html 中需要预留一个 el 区域
- main.js 把 App.vue 渲染到了 index.html 所预留的区域中
- main.js
// 导入 vue 包,得到 Vue 构造函数
import Vue from ‘vue'
// 导入 App.vue 等组件,将 App.vue 等组件的模板结构渲染到 HTML 页面中
import App from './App.vue'
// 插件 Vue 的实例对象
new Vue({
// 把 render 函数指定的组件,渲染到 HTML 页面中
// render 函数中渲染的是哪个 .vue ,哪个就是根组件
render: h => h(Test)
}).$mount('#app')
// Vue 实例的 $mount() 方法,作用和 el 属性完全一样!
4.项目打包
注意配置 vue - cli 文件,项目打包时默认是以发布到服务器为标准,故打包后是无法双击打开的,但是可以通过 vue-cli 进行配置
四. Vue 组件
1.组件化开发
根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护
- vue 是一个支持组件化开发的前端框架
- vue 中规定,组件的后缀名是 .vue 。之前接触到的 App.vue 文件本质上就是一个 vue 的组件
2. vue 组件的组成部分
- template -> 组件的模板结构
- script -> 组件的 JavaScript 行为
- style -> 组件的样式
<!-- template 中只能出现一个根元素 -->
<template>
<div class="test-box">
<h3>
这是用户自定义的 Test.vue ---{{ username }}
</h3>
<button @click='changeName'>
修改用户名
</button>
</div>
<!--
<div>
报错
</div>
-->
</template>
<script>
//默认导出。固定写法
export default {
// data 数据源
// 注意: .vue 组件中的 data 不能像之前一样,不能指向对象
// 注意: 组件中的 data 必选是个函数
data() {
// 这个 return 出去的 { } 中,可以定义数据
return {
username: 'zs',
}
},
//
methods: {
changeName() {
//在组件中,this 表示当前组件的实例对象
this.username = '哇哈哈'
}
},
// 当前组件的侦听器
watch: {},
// 当前组件的计算属性
computed: {},
// 过滤器
filters: {}
}
</script>
<!-- 启用less -->
<style lang="less">
.test-box {
background-color: pink;
h3 {
color: red;
}
}
</style>
3.组件之间的父子关系
- 组件在被封装好之后,彼此之间是互相独立的,不存在父子关系
- 在使用组件的时候,根据彼此的嵌套关系,形成了父子关系,兄弟关系
4.组件的使用
4.1注册 私有组件
-
使用 import 语法 导入需要的组件
-
使用 components 节点注册组件
-
以标签形式使用刚才注册的组件
// App.vue components: { // 如果在“声明组件”的时候,没有为组件指定 name 名称,则组件的名称默认就是“注册时候的名称” Left, Right // 以上的叫做 ‘注册名称’ } , // Right.vue export default { // 当提供了 name 属性的时候,组件的名称(vue 的调试工具里)就是 name 的值 name:'MyRight' // 以上的叫 ‘name名称’ }
注册名称:以标签的形式把注册好的组件,渲染和使用到页面结构之中;
name 名称:在调试工具内看到的名称;结合<keep-alive> 标签来实现组件缓存功能;
推荐在实际开发中,都给已使用的组件提供 name 名称,便于自己管理。
- 注意
- 通过 components 注册的是 私有子组件
- 例:在 组件A 的 components 节点下,注册了 组件F,则 组件F 只能用在 组件A 中;不能被用在 组件C 中。
- 通过 components 注册的是 私有子组件
4.2注册 全局组件
在 vue 项目的 main.js 入口文件中,通过 Vue.component() 方法,可以注册全局组件
// 导入需要注册的全局组件
import Count from '@/.....'
// 参1:字符串格式,表示组件的“注册名称”
// 参2:需要被注册的那个组件
Vue.component('MyCount',Count)
5.组件的 props
props 是组件的中的自定义属性,在封装通用组件时,合理的使用 props 可以极大的提高组件的复用性!
export default {
// 组件的自定义属性,其中定义的数据可在模板结构中使用
// 无法定义初始值
props:['自定义属性A','自定义属性B...'],
data() {
return {
}
}
}
//使用 (字符串类型)
<Count 自定义属性A="9"> </Count>
//使用 v-bind (数值类型)
<Count :自定义属性A="9"> </Count>
-
注意
- props 是只读的
- vue 规定:组件中封装的自定义属性是 只读的,程序员不能直接修改 props 的值,否则报错
- 有需要修改 props 时,可将 props 需要转存到 data 中
- props 是只读的
-
props 的 default 默认值
export default { // 组件的自定义属性,其中定义的数据可在模板结构中使用 props: { init: { // 使用 default 属性定义属性的默认值 default: 0 }, cover: { type: Object, default: function() { return { cover: 0 } } }
-
props 的 type 值类型
props:{ //自定义属性A : { /*配置选项 */ } //自定义属性B : { /*配置选项 */ } //自定义属性C : { /*配置选项 */ } init:{ // 设定默认值 default:0, //规定 init的值类型必须是 Number 数值,否则报错 type:Number, } },
-
props 的 required 必填项
props:{ //自定义属性A : { /*配置选项 */ } //自定义属性B : { /*配置选项 */ } //自定义属性C : { /*配置选项 */ } init:{ // 设定默认值 default:0, //规定 init的值类型必须是 Number 数值 type:Number, // 通过数组定义类型,其中之一即可 type:[Number,String] // 必填项校验 required:true }
6.组件之间的样式冲突问题
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
其原因:
- 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 进行呈现的
- 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素
怎么解决:
-
原理-使用自定义属性
-
<template> <div class="left-container" data-v-001> <h3 data-v-001>Left 组件</h3> <hr> <MyCount :init="1" data-v-001></MyCount> </div> </template> <script> import Count from './Count.vue'; export default { components: { Count } } </script> <style lang="less"> .left-container { padding: 0 20px 20px; background-color: orange; min-height: 250px; flex: 1; }; h3[data-v-001]{ color: red; } </style>
-
-
style 标签中添加 scoped
-
// scoped <style lang="less" scoped> .left-container { padding: 0 20px 20px; background-color: orange; min-height: 250px; flex: 1; }; h3{ color: red; } </style>
-
修改子组件的样式:
-
选择器前添加 /deep/
-
// h5[data-v-xxx] // [data-v-xxx] h5 /deep/ h3{ color: red; }
-
7.组件的生命周期/生命周期函数
生命周期:是指一个组件从 创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段
生命周期函数:是由 vue 框架提供的内置函数,会伴随组件的生命周期,自动按次序执行,强调的是一个时间点
-
生命周期图示
beforeCreate
- 不常用
created
- 常用,比如发起 Ajax 请求,获取数据,通过 this 调用 methods 中的方法,将获取的数据存入 data
beforeMount
- 几乎不被用到
mounted
- 常用,要操作 dom 须在这之后
beforeUpdata
- data 数据发送改变但 dom 未改变
updata
- 常用,data 数据发送改变且 dom 也改变
8.组件之间的数据共享
- 组件之间的最常见关系
- 父子关系
- 父 -> 子 传递数据
- 自定义属性:
props
不建议修改 props 里的值,须修改请转存
- 自定义属性:
- 子 -> 父 传递数据
- 自定义事件: 通过
$emit
触发自定义事件
- 自定义事件: 通过
- 父 -> 子 传递数据
- 兄弟关系
- 在 vue2.x 中使用 EventBus
-
- 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
- 在数据发送方,调用
bus.$emit('事件名称',要发送的数据)
方法触发自定义事件 - 在数据接收方,调用
bus.$on('事件名称',事件处理函数)
方法注册一个自定义事件
- 父子关系
9.ref 引用
ref 用来辅助开发者在 不依赖 DOM api 的情况下,获取 DOM 元素或组件的引用。
- 基础使用
// template
// 取名 + Ref
<h1 ref="myh1Ref">App 根组件</h1>
<button @click="showThis">打印this</button>
// script
methods:{
showThis() {
// console.log(this.$refsRef.myh1);
this.$refs.myh1Ref.style.color='red'
}
}
-
引用组件实例
-
this.$nextTick(cb)
—— 组件的 $nextTick(cb) 方法,会把 cb 回调函数推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的 DOM 更新完成后,再执行 cb 回调函数可以操作到最新的 DOM 元素。 -
// template <input ref="iptRef" type="text" v-if="inputVisible" @blur="showInput"> <button @click="showButton" v-if="!inputVisible">展示输入框</button> // script showButton() { this.inputVisible=!this.inputVisible //文本框拿到焦点 this.$nextTick(()=>{ this.$refs.iptRef.focus() }) }, showInput() { this.inputVisible=!this.inputVisible },
-
如果注册到
updata
中,只要 DOM 一更新,就会触发,因而在失去焦点的情况下,就会出现报错。
-
10.动态组件
动态组件就是指的就是动态切换组件的显示与隐藏
vue 提供了一个内置的
<component>
组件,专门用来实现动态组件的渲染。可以理解为一个占位符
-
实例:
<!-- template --> <button @click="comName='Left'">展示 Left</button> <button @click="comName='Right'">展示 Right</button> // 1.component 标签是 vue 内置的,作用为占位符 // 2.is 的值,表示要渲染的组件的名字 // 3.is 的值,应该是组件在 components 节点下注册的名称 <component :is="comName"></component> <!-- script --> data() { return { // 表示要展示的组件的名字 comName:'Left' } },
- 但此时组件切换时会导致组件销毁
-
使用 keep-alive
<keep-alive> <component :is="comName"></component> </keep-alive>
keep-alive
可把内部组件进行缓存
-
希望组件被激活时或者被缓存时做某些事
被缓存时,会自动触发组件的
deactivated
生命周期函数被激活时,会自动触发组件的
activated
生命周期函数// Left 组件内部 // 当组件第一次被创建的时候,既会执行 created 生命周期,也会执行 activated 生命周期 // 当组件被激活时,只会触发 activated 生命周期,不会再触发 created。因为组件没有被重新创建 activated() { console.log('组件被激活了'); }, deactivated() { console.log('组件被缓存了');
-
keep-alive
范围内的组件都会被缓存,如果只指定某些组件被缓存,则可以使用 include 属性扩展:与之对应的有 exclude 属性,指定某些组件不被缓存
注意:不可同时使用;name 名称
<!-- 名称为组件的 name 名称 ,组件名之间用英文的逗号分隔 --> <keep-alive include="MyLeft,Right"> <component :is="comName"></component> </keep-alive> // Left.vue export default { // 当提供了 name 组件的时候,组件的名称(vue 的调试工具里)就是 name 的值 name:'MyLeft' }
11.插槽
插槽(Slot)时 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望有用户指定的部分定义为插槽
-
简化使用:
<!-- Left.vue --> <h3>Left 组件</h3> <hr> <!--声明一个插槽区--> <slot name="default"></slot> <!-- App.vue --> <Left> <p>这是在 Left 组件的内容区域声明的p标签</p> </Left>
vue 官方规定:每个 slot 插槽,都要有一个 name 名称,如果省略则叫做 default
默认情况下,在使用组件的时候,提供的内容都会被填充到名字为 default 的插槽之中
-
指定内容到 default 插槽
<!-- template 不会被渲染到ui结构,是一个虚拟的元素 --> <!-- v-solt 指令不能直接用到某个元素身上,必须在 template 标签上 --> <template v-solt:default> <!-- 简写为:‘<template #default>’ --> <Left> <p>这是在 Left 组件的内容区域声明的p标签</p> </Left> </template>
-
可以给插槽设置后备内容
封装组件时,可以为预留的插槽提供后备内容,如果组件的使用者没有为插槽提供任何内容,则后备内容会失效。
<!--声明一个插槽区--> <slot name="default"> <p> 123 </p> </slot>
-
具名插槽
<!--文章的标题--> <div class="header-box"> <slot name="title"> </slot> </div> <!--文章的内容--> <div class="content-box"> <slot name="content"> </slot> </div> <!--文章的作者--> <div class="footer-box"> <slot name="author"> </slot> </div> //------------------------------- <Article> <template #title> <h3>诗</h3> </template> <template #content> <div> <p>鸡鸣寺的樱花</p> <p>楼外的蒹葭</p> <p>今晚的月色</p> </div> </template> <template #author> <div>作者:</div> </template> </Article>
-
作用域插槽(定义插槽时可以自定义属性,该自定义属性可以由使用插槽者接收到,类似 子 -> 父)scope
<!--文章的内容--> <div class="content-box"> <!--在封装组件时,为预留的<slot>提供属性对应的 值,这种用法,叫做“作用域插槽”--> <slot name="content" msg="hello vue"> </slot> </div> ----------------------- <template #content="scope"> <div> <p>鸡鸣寺的樱花</p> <p>楼外的蒹葭</p> <p>今晚的月色</p> <p>{{scope.msg}}</p> </div> </template>
-
作用域插槽的解构赋值
<!--文章的内容--> <div class="content-box"> <!--在封装组件时,为预留的<slot>提供属性对应的 值,这种用法,叫做“作用域插槽”--> <slot name="content" msg="hello vue" :user:"userinfo"> </slot> </div> data() { return { userinfo:{ name:'zs', age:'18' } } }, ----------------------- <template #content="{msg,user}" > <div> <p>鸡鸣寺的樱花</p> <p>楼外的蒹葭</p> <p>今晚的月色</p> <p>{{msg}}</p> <p>{{user.name}}</p> </div> </template>
五.ESLint
1.vue - cli 配置
- 注意勾选
Linter / Formatter
-
选择代码规范
Standard config 为标准规范
-
1.Lint on save: 保存时进行代码规范的检查
2.Lint and fix on commit:提交代码时进行代码的检查,并且自动修复问题
建议勾选 第 1 项,第 2 项不建议勾选,为了养成好的习惯。
2. .eslintrc.js
2.1rules:
规则:可在该网址进行查询配置项。
debugger 打断点,调试用
// 判断项目的打包
process.env.NODE_ENV === 'production' ? 'warn' : 'off'
2.2常见规则
- 不能有连续的空格、空行
- 文件的最后需要一个空行
- 语句的最后面不能有空格
- 根据报错所提示的文本到 ESLint 中查找
3. VScode 配置 ESLint
-
ESLint
-
settings.json
-
// ESLint 插件的配置 "editor.codeActionsOnSave": { "source.fixAll": true, }, "eslint.alwaysShowStatus": true,
-
-
-
Prettier - Code formatter (作者:Prettier)
-
settings.json
-
// prettier 插件的配置 "prettier.configPath": "C:\\Users\\Ming comity\\.prettierrc" "prettier.trailingComma": "none", "prettier.semi": false, // 每行文字超过此限制将会被迫换行 "prettier.printWidth": 300, // 使用单引号替换双引号 "prettier.singleQuote": true, "prettier.arrowParens": "avoid", // 设置 .vue 文件中,HTML 代码的格式化插件 "vetur.format.defaultFormatter.html": "js-beautify-html", "vetur.ignoreProjectWarning": true, "vetur.format.defaultFormatterOptions": { "js-beautify-html": { "wrap_attributes": false }, "prettier": { "trailingComma": "none", "semi": false, "singleQuote": true, "arrowParens": "avoid", "printWidth": 300 } },
-
-
-
.prettierrc 配置文件
-
路径:
C:\\Users\\Ming comity\\.prettierrc
-
{ "semi": false, "singleQuote": true, "bracketSpacing": true } settings.json中的配置如下: "prettier.configPath": "C:\\Users\\HP\\.prettierrc", // 安装Prettier配置 "eslint.alwaysShowStatus": true, "prettier.trailingComma": "none", "prettier.semi": false, // 每行文字个数超出此限制将会被迫换行 "prettier.printWidth": 300, // 使用单引号替换双引号 "prettier.singleQuote": true, "prettier.arrowParens": "avoid", // 设置 .vue 文件中,HTML代码的格式化插件 "vetur.format.defaultFormatter.html": "js-beautify-html", "vetur.ignoreProjectWarning": true, "vetur.format.defaultFormatterOptions": { "prettier": { "trailingComma": "none", "singleQuote": true, "semi": false, "arrowParens": "avoid", "printWidth": 300 }, "js-beautify-html": { "wrap_attributes": false }, },
-
-
注意用 VScode 打开该项目的目录才能有效
六. Vue - Router
1.前端路由的概念与原理
路由(英文:router)就是对应关系
前端路由:Hash 地址(#锚链接
可产生浏览历史
)与组件之间的对应关系。location.hash
-
工作方式
- 用户点击了页面上的路由链接(a链接)
- 导致了 URL 地址栏中的 Hash 值发生了变化
- 前端路由监听到了 Hash 地址的变化
- 前端路由把由当前 Hash 地址对应的组件渲染到浏览器中
// 监听 hash 变化 window.onhashchange = () => { console.log('hash 变化',location.hash); }
2.安装和配置
vue - router 是 vue 官给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。
-
安装 vue-router 包
npm i vue-router -S
-
创建路由模块
-
在 src 源代码目录下,新建 router/index.js 路由模块,并初始化以下代码
-
// 1.导入 Vue 和 VueRouter import Vue from 'vue' import VueRouter from 'vue-router' // 2.调用 Vue.use() 函数,把 VueRouter 安装为 Vue 的插件 // Vue.use() 就是用来安装插件的 Vue.use(VueRouter) // 3.创建路由的实例对象 const router = new VueRouter() // 4.向外共享路由的实例对象 export default router
-
-
导入并挂载路由模块
-
在 main.js 入口文件中
-
import router from '@/router/index.js' new Vue({ render: h => h(App), // 在 vue 项目中,要想把路由用起来,必须把路由实例对象,通过下面的方式进行挂载 router }).$mount('#app')
-
-
声明路由链接和占位符
-
在 App.vue 根组件中
-
<!-- 当安装了 vur-router 后,就可以使用 router-link 来替代掉普通的 a 链接--> <!-- ! <a href="#/home">首页</a> ! <a href="#/movie">电影</a> ! <a href="#/about">关于</a> --> <!-- 不用‘#’号 --> <router-link to="/home">首页</router-link> <router-link to="/movie">电影</router-link> <router-link to="/about">关于</router-link> <hr /> <!-- 只要在项目中安装和配置了 vue-router ,就可以使用 router-view 这个组件了 --> <!-- 作用很单纯,就是个:占位符 --> <router-view></router-view>
-
在 router/index.js 路由模块中定义“hash 地址”与“组件”之间的对应关系
-
// 导入需要的组件 import Home from '@/components/Home.vue' import Movie from '@/components/Movie.vue' import About from '@/components/About.vue' // 2.调用 Vue.use() 函数,把 VueRouter 安装为 Vue 的插件 Vue.use(VueRouter) // 3.创建路由的实例对象 const router = new VueRouter({ // routes 是一个数组,作用:定义“hash 地址”与“组件”之间的对应关系 routes:[ // 路由规则 不用‘#’号 {path: '/home',component: Home}, {path: '/movie',component: Movie}, {path: '/about',component: About} ] })
-
3.常见用法
3.1路由重定向
- 用于解决访问主域名加端口时,跳转到哪个锚链接的问题
路由重定向指的是:用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。
-
通过路由规则的 redirect 属性,指定一个新的路由地址
// 3.创建路由的实例对象 const router = new VueRouter({ // routes 是一个数组,作用:定义“hash 地址”与“组件”之间的对应关系 routes:[ // 当用户访问 / 的时候,通过 redirect 跳转到 /home {path: '/',redirect: '/home'}, {path: '/home',component: Home}, {path: '/movie',component: Movie}, {path: '/about',component: About} ] })
3.2路由嵌套
通过路由实现组件的嵌套展示,叫做嵌套路由。
-
通过 children 属性声明子路由规则
// 子路由组件 import Tab1 from '@/components/tabs/Tab1.vue' import Tab2 from '@/components/tabs/Tab2.vue' // 3.创建路由的实例对象 const router = new VueRouter({ // routes 是一个数组,作用:定义“hash 地址”与“组件”之间的对应关系 routes:[ // 当用户访问 / 的时候,通过 redirect 跳转到 /home {path: '/',redirect: '/home'}, {path: '/home',component: Home}, {path: '/movie',component: Movie}, { // about 页面的路由规则(父级路由规则) path:'/about', component:About, children:[ // 通过 children 属性,嵌套子级路由规则 注意没有 ‘/’ {path:'tab1',component:Tab1}, {path:'tab2',component:Tab2} ] } ] })
-
子路由重定向
{ // about 页面的路由规则(父级路由规则) path:'/about', component:About, redirect: '/about/tab1', children:[ // 通过 children 属性,嵌套子级路由规则,注意没有 ‘/’ {path:'tab1',component:Tab1}, {path:'tab2',component:Tab2} ]
-
默认子路由
- 如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
{ // about 页面的路由规则(父级路由规则) path:'/about', component:About, // redirect: '/about/tab1', children:[ // 通过 children 属性,嵌套子级路由规则,注意没有 ‘/’ {path:'',component:Tab1}, {path:'tab2',component:Tab2} ]
- About.vue
// 可以删除‘/tab1’ <router-link to="/about">tab1</router-link> <router-link to="/about/tab2">tab2</router-link>
3.3动态路由匹配
-
思考
动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。
-
使用**英文的冒号(:)**来定义路由的参数项
// router/index.js // 路由中的动态参数以 : 进行声明,冒号后面的是动态参数的名称 // 需求:在 Movie 组件中,希望根据 id 的值来展示对应电影的详情信息。 {path: '/movie/:id',component: Movie} // 相比于上面的定义 3 个路由规则,合并成了一个,提高了路由规则的复用性
<!-- 注册组件 App.vue --> <router-link to="/home">首页</router-link> <router-link to="/movie/1">洛基</router-link> <router-link to="/movie/2">妇联</router-link> <router-link to="/movie/3">雷神</router-link> <router-link to="/about">关于</router-link>
- 方式1 -- 在 Movie.vue 组件里拿到 id 值
<!-- Movie.vue --> <!-- 拿到路由的 参数对象 ,后拿到 id 值 --> <h3>Movie 组件---{{this.$route.params.id}}</h3>
- 方式2 -- 在 Movie.vue 组件里拿到 id 值
<!-- Movie.vue --> <template> <h3>Movie 组件---{{id}}</h3> </template> <srcipt> export default { name: 'Movie', props:['id'], methods: { showThis() { console.log(this); } } } </srcipt>
// router/index.js // 开启 props 传参 {path: '/movie/:id',component: Movie,props:true},
3.4扩展
<router-link to="/movie/1">洛基</router-link>
注意1:在 hash 地址中, / 后面的参数项,叫做“路径参数”
在路由“参数对象”中 ,需要使用 this.$router.params 来访问路径参数
<router-link to="/movie/2?name=zs&age=20">妇联</router-link>
注意2:在 hash 地址中, ? 后面的参数项,叫做“查询参数”
在路由“参数对象”中,需要使用 this.$router.query 来访问查询参数
注意3:在 this.$route 中, path 只是路径部分:fullPath 是完整的地址
3.5声明式导航 & 编程式导航
在浏览器中,点击链接实现导航的方式,叫做声明式导航。
例:
- 普通网页中点击 <a> 链接、vue 项目中点击 <router-link> 都属于声明式导航
在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。
- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
this.$route
路由对象
this.$router
导航对象
-
vue-router 中的编程式导航 API
-
this.$router.push('hash 地址')
- 跳转到指定 hash 地址,并增加一条历史记录
-
this.$router.replace('hash 地址')
- 跳转到指定 hash 地址,并替换掉当前的历史记录
-
this.$router.go(数值 n)
- 调用该方法可在浏览器中,前进或后退历史记录
// go(-1) 表示后退一层 // 当后退的层数过大时,会原地不动 // go(1) 表示前进一层 this.$router.go(-1)
-
简化用法
在实际开发中,一般只会前进和后退一层页面,因此 vue-router 提供如下两种便捷方法
$router.back()
- 在历史记录中,后退到上一个页面
$router.forward()
- 在历史记录中,前进到上一个页面
-
3.6导航守卫
导航守卫可以控制路由访问权限。
-
全局前置守卫
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制
// router/index.js // 创建路由实例对象 const router = new VueRouter({ ... }) const fn = (to, from, next) => { // to 是将要访问的路由的信息对象 // from 是将要离开的路由的信息对象 // next 是一个函数,调用 next() 表示放行,允许这次路由导航 } // 调用路由实例对象的 beforeEach 方法,即声明“全局前置守卫” // 每次发送路由导航跳转的时候,都会自动触发 fn 这个“回调函数” router.beforeEach(fn)
-
当用户拥有后台主页的访问权限,直接放行:
next()
-
当前用户没有后台主页的访问权限,强制跳转到登录页面:
next('/login')
-
当前用户没有后台主页的访问权限,不允许跳转到后台主页:
next(false)
const fn = (to, from, next) => { // to 是将要访问的路由的信息对象 // from 是将要离开的路由的信息对象 // next 是一个函数,调用 next() 表示放行,允许这次路由导航 // 1.要拿到用户将要访问的 hash 地址 // 2.判断 hash 地址是否等于 /main // 2.1 如果等于 /main,证明需要登录后,才能访问成功 // 2.2 如果不等于 /main,则不需要登录,直接放行 next() // 3. 如果访问的地址是 /main.则需要读取 localStorage 中的 token 值 // 3.1 如果有 token,则放行 // 3.2 如果没有 token,则强制跳转到 /login 登录页 const pathArr = ['/home','/home/users'] // 也可将 hash 地址另存为一个 js / json 文件,导入后使用 if(pathArr.indexOf(to.path) !== -1) { const token = localStorage.getItem('token') if(token) { next() } else { next('/login') } } else { next() } }
-
七.组件库
1.Vant
八.VueX
https://juejin.cn/post/6928468842377117709?searchId=20230809094812BFF8B833CA8292213056
九.建议
-
属性小驼峰改成连字符
<!-- 在使用组件时,如果某个类名是小驼峰格式,建议改写成连字符,例如 cmtCount 建议写成 cmt-count --> <ArticleInfo v-for="item in resArr" :key="item.id" :autName="item.aut_name" :title="item.title" :cover="item.cover" :comm-count="item.comm_count" :pubDate="item.pubdate"></ArticleInfo>
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
述文本描述文本描述文本描述文本描述文本描述文本
述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
查看全部3条回复
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本