vue 特性(初体验)
数据驱动视图
数据的变化会驱动视图自动更新,只需要把数据维护好,会自动渲染。
双向数据绑定
在网页中,表单需要采集数据,Ajax负责提交数据
好处:开发者不再需要手动操作 DOM 元素,来获取表单元素最新的值!
MVVM 底层了解
MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。MVVM 指的是 Model、View 和 ViewModel
- Model 表示当前页面渲染时所依赖的数据源。
- View 表示当前页面所渲染的 DOM 结构。
- ViewModel 表示 vue 的实例,它是 MVVM 的核心。
ViewModel 作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。
- 当数据源发生变化时,会被 ViewModel 监听到
- VM 会根据最新的数据源自动更新页面的结构
- 当表单元素的值发生变化时,也会被 VM 监听到
- VM 会把变化过后最新的值自动同步到 Model 数据源中
初体验,渲染数据
<body>
<!--希望vue拿到控制这个div,渲染数据-->
<!--固定语法-->
<div id="app">{{username}}</div>
<!-- 导入Vue库文件 -->
<script src="js/vue-2.6.12.js"></script>
<!--创建vue的实例对象-->
<script>
const vm = new Vue({
// el 固定写法,表示当前vm实例控制页面的那个区域,接收一个选择器
el: '#app',
// data 要渲染到页面上的数据
data: {
username: "zhangsan"
}
})
</script>
</body>
上面什么是view,什么是model,什么是 ViewModel呢
- el是 view
- data 是model
- viewmodel 是构造函数
安装Vue调试工具,打开访问文件URL地址
指令与过滤器
辅助开发者渲染页面的基本结构
内部渲染指令
要先写好数据,用vue语法,一定要在vue控制范围内,比如#app
- v-test
- 会覆盖元素内部原有的内容!
<body>
<div id="app">
<p v-text="username"></p>
<p v-text="gender">性别</p>
</div>
</body>
- {{}}
- 插值语法,不会覆盖
<body>
<div id="app">
<p>名字:{{username}}</p>
<p>性别:{{gender}}</p>
</div>
</body>
- v-html
- 以上的方式不能渲染标签,只是纯文本
<div id="app">
<div v-text="info"></div>
<div v-html="info"></div>
</div>
属性绑定指令
有时候我们需要给属性赋值,就需要 v-bind 指令,可以简写成 :
<div id="app">
<input type="text" v-bind:placeholder="tips">
<img :src="imgs" alt="" width="120px">
</div>
<!-- 导入Vue库文件 -->
<script src="js/vue-2.6.12.js"></script>
<!--创建vue的实例对象-->
<script>
const vm = new Vue({
// el 固定写法,表示当前vm实例控制页面的那个区域,接收一个选择器
el: '#app',
// data 要渲染到页面上的数据
data: {
tips: "请输入用户名:",
imgs: "http://localhost:8080/rem/images/logo.png"
}
})
- 那v-bind指令中到底可以写什么呢,其实就是JS代码,运算等
<div id="app" :title="'box' + index">
<p>{{tips}},反转之后:{{tips.split('').reverse().join('')}}</p>
</div>
v-on 绑定事件
- 绑定点击事件 v-on:click
<body>
<div id="app">
<!-- 给按钮添加点击事件 -->
<button v-on:click="add"></button>
</div>
<script src="js/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
// 这里定义事件的处理函数
methods: {
add: function () {
console.log('ok')
}
}
})
</script>
</body>
- 简化函数书写
const vm = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add() {
console.log('ok')
}
}
})
- v-on: 简化绑定事件书写
<button @click="add(1)">+1</button>
- 修改 vue 中 data 里面的值
<script>
const vm = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add() {
console.log(this === vm);
// vm.count += 1; 不推荐
this.count += 1;
},
sub() {
this.count -= 1;
},
}
})
</script>
- 如何传参
<div id="app">
<button v-on:click="add(1)">+1</button>
</div>
注意:原生 DOM 对象有 onclick、oninput、onkeyup 等原生事件
替换为 vue 的事件绑定形式后
分别为:v-on:click、v-on:input、v-on:keyup
怎么获得事件源
方式一:当没有传递任何参数时获得的是事件源,传递则覆盖
<div id="app">
<button @click="add">+1</button>
</div>
<script src="js/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
// 当没有传递任何参数时获得的是事件源
add(e) {
console.log(e)
this.count += 1
if (this.count % 2 === 0) {
e.target.style.backgroundColor = "red"
} else {
e.target.style.backgroundColor = ""
}
}
}
})
</script>
方式二:使用内置变量 $event ,获得事件源
<body>
<div id="app">
<button @click="add(1, $event)">+1</button>
</div>
<script src="js/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add(n, e) {
console.log(e)
this.count += 1
if (this.count % 2 === 0) {
e.target.style.backgroundColor = "red"
} else {
e.target.style.backgroundColor = ""
}
}
}
})
</script>
</body>
事件修饰符
- 阻止默认行为 .prevent
<button @click.prevent="add(1, $event)">+1</button>
- 阻止事件冒泡:就是两个事件嵌套,同时触发就是冒泡
触发谁给谁添加
<button @click.stop="sea">按钮</button>
按键修饰符 keyUp 和 keyDown
<body>
<div id="app">
<input type="text" @keyup.esc="clear">
</div>
<script src="js/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
clear(e) {
e.target.value = ''
}
}
})
</script>
双向数据绑定指令
就是双向数据互通,修改任何一方都会改变
使用场景
- input 输入框
- type="radio"
- type="checkbox"
- type="xxxx"
- textarea
- select
<div id="app">
<p>{{ count }}</p>
<input type="text" v-model="count">
<select name="" id="" v-model="count">
<option value="">请选择:</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
</div>
<script src="js/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
count: 0
}
})
</script>
</body>
v-model 修饰符
- .number 自动将值转为数字
<input type="text" v-model.number="n1">
- .trim 去除两边空格
<input type="text" v-model.trim="n1" @keyup.enter="submit">
- .lazy 延迟更新,不会和以前一样,立刻同步数据
<input type="text" v-model.lazy="n1" @keyup.enter="submit">
条件渲染指令
- v-if 结果为false,会删除元素(频繁切换状态,性能好)
- v-show 结果为false,会隐藏元素(后期可能不需要展示性能较好)
<div id="box">
<p v-if="network">v-if控制的</p>
<p v-show="network">v-show控制的</p>
</div>
- v-else
<div V-if="type ===A'">优秀</div>
<div V-else-if="type ===B'">良好</div>
<div V-else-if="type === C'">一般</div>
<div V-else>差</div>
v-for 循环渲染
- index 表示当前索引,自己也可以访问
<table class="table table-bordered table-hover table-striped">
<tr v-for="(item, index) in list" :title="item.name">
<td>{{index}}</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</table>
- :key 维护列表状态
当列表的数据变化时,默认情况下,vue 会尽可能的复用已存在的 DOM 元素,从而提升渲染的性能。但这种 默认的性能优化策略,会导致有状态的列表无法被正确更新。
<ul>
<!-- 加 key 属性的好处:
<!-- 1.正确维护列表的状态 --
<!-- 2.复用现有的 DOM 元素,提升渲染的性能 -->
<li V-for="user in userlist" :key="user .id">
<input type="checkbox” />
姓名: {{user.name}}
</li>
</ul>
- key 的值只能是字符串或数字类型
- key 的值必须具有唯一性(即:key 的值不能重复)
- 建议把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性)
- 使用 index 的值当作 key 的值没有任何意义(因为 index 的值不具有唯一性)
- 建议使用 v-for 指令时一定要指定 key 的值(既提升性能、又防止列表状态紊乱)
总结,技巧
- label 的绑定可用插值语法实现循环绑定
- 数组的去掉某一项快速
this.list = this.list.filter(item => item.id !== id)
- 阻止表单默认行为,并触发事件
<form @submit.prevent="add">
- 如果往数组里添加,怎么保持ID一致性。
- 提前创建一个变量保存着
监听器(watch)
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
- 下面是方法监听器,缺点无法在进入页面的时候就触发一次
- 如果监听的是对象,那么对象的属性发生变化,不会触发监听器
<script>
let vue = new Vue({
el: '#app',
data: {
username: ''
},
watch: {
// 第一个是新值,第二个是旧值,每当username这个属性值被修改的时候,就会触发函数
username(newValue, oldValue) {
console.log(newValue)
console.log(oldValue)
}
}
});
</script>
有个需求,希望第一次也触发监听器。
- 对象监听器,好处进入页面自动触发监听器
- immediate: true 表示已进入页面就触发一次,默认false
- deep:true,可以深度监听对象里面属性的变化
<script>
let vue = new Vue({
el: '#app',
data: {
username: 'admin'
},
watch: {
username: {
handler(newValue, oldValue) {
console.log(newValue)
console.log(oldValue)
},
// true 表示已进入页面就触发一次,默认false
immediate: true
}
}
});
</script>
对象监听
<script>
let vue = new Vue({
el: '#app',
data: {
info: {
username: 'admin'
}
},
watch: {
info: {
handler(newValue, oldValue) {
console.log(newValue)
console.log(oldValue)
},
// 深度监听对象里面的信息,一个改变,就触发
deep: true
}
}
});
</script>
简化上面的方式
- 'info.username' 必须用单引号括起来
<script>
let vue = new Vue({
el: '#app',
data: {
info: {
username: 'admin'
}
},
watch: {
'info.username'(newValue, oldValue) {
console.log(newValue)
console.log(oldValue)
}
}
});
</script>
计算属性(computed)
计算属性指的是通过一系列运算之后,最终得到一个属性值。 这个动态计算出来的属性值可以被模板结构或 methods 方法使用。
- 声明的时候是方法,但是使用的时候已经转换为属性了。
- 发生变化会自动随着变化,当作普通属性即可
<!-- 专门用户呈现颜色的 div 盒子 -->
<div class="box" :style="{ backgroundColor: rgb }">
{{rgb}}
</div>
<button @click="show">按钮</button>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
// 红色
r: 0,
// 绿色
g: 0,
// 蓝色
b: 0
},
methods: {
// 点击按钮,在终端显示最新的颜色
show() {
console.log(this.rgb)
}
},
// 所有计算属性,都要定义到 computed节点下
// 计算属性在定义的时候,要定义成方法格式
computed: {
/// rgb,定义号返回需要的内容
rgb() {
return (`rgb(${this.r},${this.g},${this.b})`)
}
}
});
console.log(vm)
</script>
过滤器(vue3已经砍掉了)
过滤器 (Filters)是 vue 为开发者提供的功能,常用于文本的格式化。
过滤器可以用在两个地方: 插值表达式和 v-bind 属性绑定
<!-- 在双花括号中通过“管道符”调用 capitalize 过滤器,对 message 的值进行格式化 -->
<p>{{ message | capitalize }}</p>
<!-- 在 v-bind 中通过“管道符”调用 formatId 过滤器,对 rawId 的值进行格式化 -->
<div v-bind:id="rawId formatId"></div>
- 定义方式
<body>
<div id="app">
<p>{{ message | cap1 }}</p>
</div>
<script src="vue-2.6.12.js"></script>
<script>
const vue = new Vue({
el: "#app",
data: {
message: "hello vue!"
},
//过滤器函数,必须定义在 filters下
// 过滤器本质就是函数,有一个形参,就是管道符前面的值
filters: {
cap1(val) {
let first = val.charAt(0).toUpperCase();
let splice = val.slice(1);
// 一定有返回值
return first + splice
}
}
});
</script>
</body>
私有过滤器和全局过滤器
私有过滤器,只能被当前vue控制的标签所解析
如果全局过滤器和私有过滤器名字重复,私有会覆盖全局
推荐只使用全局过滤器
Vue.filter("cap1", function (val) {
let first = val.charAt(0).toUpperCase();
let splice = val.slice(1);
// 一定有返回值
return first + splice + "`````"
})
格式化时间
<td>{{ item.time | dataFormat }}</td>
Vue.filter("dataFormat", function (val) {
return dayjs(val).format('YYYY-MM-DD HH:mm:ss')
})
可以连续多调用
<!-- 把 message 的值,交给 filterA 进行处理
<!-- 把 filterA 处理的结果,再交给 filterB 进行处理 -->
<!-- 最终把 filterB 处理的结果,作为最终的值渲染到页面上 -->
{{ message | filterA | filterB )
传参
<!-- arg1 和 arg2 是传递给 filterA 的参数
<p>{{ message filterA(arg1,arg2) }}</p>
//过滤器处理函数的形参列表中:
//第一个参数:永远都是”管道符“前面待处理的值
//从第二个参数开始,才是调用过滤器时传递过来的 arg1 和 arg2 参数
Vue.filter('filterA' , (msg, arg1, arg2) => (
// 过滤器的代码逻辑...
})
axios(发送网络请求)
艾克sei奥斯
并不是服务器返回的真实数据,里面的 .data才是
<script src="lib/axios.js"></script>
<script>
// 获得Promise对象
let result = axios({
method: 'GET',
url: 'http://localhost:8081/tCustomerLoss/list'
});
// 获取结果
result.then(function (data) {
console.log(data)
})
</script>
传递参数
// URL 查询参数
param: {id: 1},
// 请求体参数
data: {}
GET请求
<script>
// 获得Promise对象
axios({
method: 'GET',
url: 'http://localhost:8081/tCustomerLoss/383',
param: {
id: 1
}
}).then(function (data) {
console.log(data.data)
})
</script>
POST请求
<body>
<div id="app">
<button id="btn">post请求</button>
</div>
<script src="lib/axios.js"></script>
<script src="lib/dayjs.min.js"></script>
<script src="lib/vue-2.6.12.js"></script>
<script>
document.querySelector('#btn').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
// await 只能用在被 async “修饰”的方法中
const res = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
});
console.log(res)
})
</script>
</body>
简化上面的,直接获取data对象(解构赋值)
document.querySelector('#btn').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
// await 只能用在被 async “修饰”的方法中
const {data} = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
});
console.log(data)
})
重命名
把解构出来的 data 属性,使用冒号进行重命名,一般都重名命为 {data: res}
const {data : res} = await axios({
快速发起请求
<div id="app">
<button id="btnPost">post请求</button>
<button id="btnGet">get请求</button>
</div>
<script src="lib/axios.js"></script>
<script>
document.querySelector('#btnPost').addEventListener('click', async function () {
// post直接写
const {data: res} = await axios.post("http://www.liulongbin.top:3006/api/post", {name: 'zs', gender: "女"})
console.log(res)
})
// get需要加param
document.querySelector("#btnGet").addEventListener('click', async function () {
const {data: res} = await axios.get("http://www.liulongbin.top:3006/api/getbooks", {
param: {id: 1}
})
console.log(res)
})
</script>
Vue-cli
简化了程序员基于webpack创建工程化的Vue项目过程
单页面程序
什么是单页面应用程序
指的是一个 Web 网站中只有唯一的一个 HTML 页面,所有的功能 与交互都在这唯一的一个页面内完成
安装 vue -cli
npm install -g @vue/cli
创建项目
vue create 项目的名称
项目搭建
还可以使用vue ui命令创建,效果一样是可视化的
- 选择模式
第一个vue2,第二个vue3,第三个自定义(推荐)
- 选择功能
- 第一个是选择vue版本(必选)
- 第二个是Babel,解决js兼容性(必选)
- 第三个是微软脚本语言,比js强大,是ts
- 第七个是less,用less写
- 第八个是约束团队编写风格,比如:喜欢单引号或者双引号(不推荐)
- 后面是测试工具
- 选择vue版本
- 选择预处理器(常用less)
- 选择插件配置文件存放位置
- 每个插件是独立存放,还是和package.json放一起,选独立
- 是否保存,刚刚选择配置的内容,根据自己需求
运行项目 / 目录介绍
npn run serve
- node_modules 项目源代码
- public
- favicon.ico 图标
- index.html 主文件
- src 源代码目录
- assets 静态资源,图片,样式表
- components 程序员封装的可复用的组件,放这里
- main.js 入口,整个项目运行前执行main.js文件
- app.vue 是项目的根组件
vue项目运行流程
在工程化的项目中,vue 通过 main.js 把 App.vue 渲染到 index.html 的指定区域中。
- App.vue 用来编写待渲染的模板结构
- index.html 中需要预留一个 el 区域
- main.js 把 App.vue 渲染到了 index.html 所预留的区域中
导入vue包获得vue构造函数
import Vue from 'vue'
new Vue({
// 渲染的谁谁就是根组件且唯一,下面是APP
render: h => h(App),
//和el指定一样
}).$mount('#app')
每个 .vue 组件都由 3 部分构成,分别是:
- template -> 组件的模板结构
- script -> 组件的 JavaScript 行为
- style -> 组件的样式
// 定义结构
<template>
<div>
<div class="test-box">
<h3>这是用户自定义的 Test.vue --- {{ username }}</h3>
<button @click="chagneName">修改用户名</button>
</div>
<div>123</div>
</div>
</template>
// 定义行为
<script>
// 默认导出。这是固定写法!
export default {
// data 数据源
// 注意:.vue 组件中的 data 不能像之前一样,不能指向对象。
// 注意:组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 { } 中,可以定义数据
return {
username: 'admin'
}
},
methods: {
chagneName() {
// 在组件中, this 就表示当前组件的实例对象
console.log(this)
this.username = '哇哈哈'
}
},
// 当前组件中的侦听器
watch: {},
// 当前组件中的计算属性
computed: {},
// 当前组件中的过滤器
filters: {}
}
</script>
//定义样式
<style lang="less">
.test-box {
background-color: pink;
h3 {
color: red;
}
}
</style>
组件之间的父子关系
组件只在使用时会产生父子兄弟关系
- 导入需要的组件
// 导入需要的组件
import Left from '@/components/Left'
- 使用components 节点注册组件
components: {
// "Left": Left, 可以简写
Left // 简写
}
- 以标签的形式使用组件
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<left></left>
</div>
形成了父子关系,通过components注册的组件是私有组件(非常麻烦,不利于扩展)
注意:在A组件中注册的组件,只能在A中使用,不能在组件C中使用
注册全局组件
// 注册全局组件
import Count from "@/components/Count";
// 载入组件
Vue.component('MyCount', Count)
然后直接标签使用即可,无法自己使用自己组件,会抛出异常
props 自定义属性,只读,用于组件设置初始值
父组件向子组件传入9,子组件属性中就可以直接使用
export default {
props: ["init"],
}
直接传递,传递过去的是字符串,传数值,需要加:使用v-bind形式
<MyCount :init="9"></MyCount>
那么props的值是可以被修改的吗?答案是不能的,它是只读的,修改会抛出异常,但是可能会影响功能。会抛出异常,下面存入count中就可以了
data() {
return {
// 把 props 中的 init 值,转存到 count 上
count: this.init
}
},
上面这种方式啊,如果子组件没有传递,那就是undefined的了,没有默认值,设置默认值是这样的。
type:类型, 规定传递的数据必须是指定类型,否则抛出异常
props: {
// 属性
init: {
// 参数 default 默认值
default: 0,
// 规定传递的数据必须是指定类型,否则抛出异常
type: Number // Boolean,Array,Object,String
}
},
required:必填项,和默认值无关系,没传就是报错
props: {
// 属性
init: {
// 要求使用该组件,必须传递该数据
required: true
}
},
对象类型指定默认值
cover: {
type: Object,
default: function () {
return { type: 0 }
}
}
组件之间样式冲突
指在A页面修改了样式,影响到了B页面的样式
原因:vue中组件的样式会全局生效的,只有唯一一个index.html,最终都会到一个页面中。
解决方案:需要加一个特定的属性
<div class="left-container" v-data-001>
解决方案2:给style加一个属性scoped
Vue 官方已宣布 /deep/ 已被遗弃,我们使用了 ::v-deep 选择器来向下深度覆盖样式
::v-deep .el-table .cell {
white-space: nowrap;
}
<style lang="less" scoped>
父组件的子组件不会和父组件在同一个空间,导致子组件的样式不会生效,怎么解决?
/deep/ h5 {
color: pink;
}
这个deep 表示父组件的 v-data-ID,所以这个就会生效,包含该ID后代的H5
使用场景:当使用第三方组件库的时候,如果有需要修改默认样式的要求,需要用到/deep/
vue-template-compiler 包
它会把每个vue文件转换成js文件,交给浏览器去执行
组件的生命周期
以标签形式,使用到了一个组件,就会创建了一个实例
- 生命周期:创建 -> 运行 -> 销毁的整个阶段
- 生命周期函数:由vue框架提供内置函数,会伴随着组件的生命周期,自动按次序执行
vue生命周期运行流程
- webpack 从main.js 开始打包,发现用到了app,把App创建一个实例出来
- App里面用到的组件一直,webpack把这些组件全部解析成js文件
- 生成的文件被浏览器解析执行,渲染到页面上
下面这个图,就是创建一个实例的整个过程
beforeCreate 第一个生命周期函数(不重要)
初始化事件和生命周期函数
<script>
export default {
// 第一个生命周期函数
beforeCreate() {
// 这个阶段的属性还没有被初始化,无法调用,会抛出异常
console.log(this.info);
// 这个message是 undefined
console.log(this.message);
// 这个阶段的属性还没有被初始化,无法调用,会抛出异常
this.show()
}
}
</script>
created 初始化数据的函数(重要)
这个阶段可以发起ajax发起请求拿数据,经常用它调用methods的方法,请求服务器拿数据,转存data中,供template模板渲染使用
- 注意这个时候不能操作dom元素,拿到的全是null,因为结构还没有渲染
created() {
// 都可以正常输出
console.log(this.info);
console.log(this.message);
this.show()
}
发送Ajax请求
<template>
<div class="controller">Test 测试----{{ books.length }} 本图书</div>
</template>
<script>
export default {
data() {
return {
// 定义空数组,存储图书
books: []
}
},
methods: {
// 发送ajax请求,拿数据
initBookList() {
const request = new XMLHttpRequest();
request.addEventListener('load', () => {
const parse = JSON.parse(request.responseText);
this.books = parse.data
console.log(parse)
})
request.open('GET', 'http://www.liulongbin.top:3006/api/getbooks')
request.send()
}
},
// 初始化数据
created() {
this.initBookList()
},
}
</script>
渲染模板
首先判断vue构造函数中,有没有el属性,有的话就就使用,没有就初始化一个,然后继续判断有没有template模板,有的话就基于模板,初始化结构,没有使用默认的。
就是基于数据和模板在内存中编译生成HTML结构
beforeMount 准备渲染界面(不重要)
还没有渲染,即将渲染,打印的是null
beforeMount() {
const element = document.querySelector("#myh3");
console.log(element)
}
mounted 可以操作dom(重要)
// 完成了HTML结构的渲染,此时可以操作dom了
mounted() {
const element = document.querySelector("#myh3");
console.log(element)
}
组件运行阶段
beforeUpdate(data中数据每更新一次都会触发),数据是最新的,但是结构是旧的
第一次渲染也会触发,数据是最新的了,但是结构还没有重新渲染
// 每次数据发送改变会执行这个函数
beforeUpdate() {
console.log("beforeUpdate")
}
updated(操作最新的元素使用)
数据是最新的,结构也是重新渲染之后的。都是最新的,这是一个圈,他们只要数据变化了,每一个函数都会被执行一次,最多n次最少0次
组件传值,数据共享
最常见关系:父子关系,兄弟关系
父向子传值(值都不建议修改)
普通值:是深复制,对象:是浅拷贝 父组件
<left :user="user"></left>
子组件
props: ['user'],
子向父传值
使用的自定义事件
子组件
methods: {
add() {
this.count += 1
// 将count传递给父组件
this.$emit('numchange', this.count)
}
}
父组件
<template>
<div class="app-container">
<div class="box">
// 第一步先定义,这个名称要和子组件传递的key一致
<Right @numchange="getNewCount"></Right>
</div>
</div>
</template>
<script>
export default {
data() {
return {
countFromSon: null
}
},
// 定义方法拿到值
methods: {
getNewCount(val) {
this.countFromSon = val
}
}
}
</script>
兄弟之间互相传值
在vue2中用的是EventBus
- 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
- 在数据发送方,调用 bus.$emit('事件名称', 要发送的数据) 方法触发自定义事件
- 在数据接收方,调用 bus.$on('事件名称', 事件处理函数) 方法注册一个自定义事件
发送方组件
<template>
<div class="left-container">
{{ message }}
<button @click="send">发送好诗</button>
</div>
</template>
<script>
// 导入eventBus.js 模板
import bus from './eventBus'
export default {
data() {
return {
// 把这个发送给right组件
message: '黑云压城城欲摧'
}
},
// 发送
methods: {
send() {
// 这个key值一定保持一致
bus.$emit('share', this.message)
}
}
}
</script>
接收方
<template>
<div class="right-container">
<p>{{ message }}</p>
</div>
</template>
<script>
// 导入 eventBus.js 模块
import bus from './eventBus'
export default {
data() {
return {
message: ''
}
},
created() {
// 为bus绑定自定义事件,val就是数据
bus.$on('share', (val) => {
console.log('share被触发勒' + val)
this.message = val
})
}
}
</script>
eventBus.js 文件
import Vue from 'vue'
// 共享函数
export default new Vue
传值2 provide 组件向子孙组件传值
实现响应式的方式,在data中定义对象,将对象赋值给 provide 中,修改对象子孙组件的值将同步修改,非对象不可以
父组件
// 这是一个 Vue.js 组件的配置对象
export default {
// 定义该组件的名称为 "App",通常用于开发和调试目的
name: 'App',
// 使用 provide 选项向子孙组件提供数据或函数
provide() {
return {
// 提供一个名为 'color' 的数据,设置为 'pink'
color: 'pink',
// 提供一个名为 'userinfo' 的数据,包含 'name' 和 'age' 属性
userinfo: {
name: 'zs',
age: 18
}
}
}
}
子孙组件
// 这是一个 Vue.js 组件的配置对象
export default {
// 定义该组件的名称为 "HelloWorld",通常用于开发和调试目的
name: 'HelloWorld',
// 使用 inject 选项来接收父组件提供的数据
// 在这里,我们接收了两个属性:'color' 和 'userinfo'
inject: ['color', 'userinfo']
}
父组件调用子组件的方法
先加 ref
<component ref="child"></component>
触发方法
const series = this.$refs.child.方法名(参数)
ref 引用(操作dom)
不依赖jquery操作dom,每一个组件实例上,都包含一个refs指向空对象
获取dom
<h1 ref="myh1">App 根组件</h1>
methods: {
showThis() {
// 获取上面的dom
console.log(this.$refs.myh1)
}
},
还可以引用实例
<Left ref="comLeft"></Left>
refresh() {
// 获取实例并将实例的属性值设置为0
this.$refs.comLeft.count = 0
}
监听事件
blur:当失去焦点的时候,触发函数
<input type="text" @blur="showButton" v-else/>
延迟执行 & 自动获取焦点
延迟到页面重新渲染时执行
- this.$nextTick 延迟执行函数
this.$nextTick(() => {
// 获取dom为input并且自动获取焦点focus
this.$refs.inputRef.focus()
})
some循环 & forEach 循环 & every循环
forEach 是不能终止循环的
const str = ['小红','大红','苏大强', '宝'];
str.forEach((item, index) => {
console.log(item)
if(item === '苏大强'){
console.log(index)
}
})
some 循环可以终止循环
const str = ['小红','大红','苏大强', '宝'];
str.some((item, index) => {
console.log(item)
if(item === '苏大强'){
console.log(index)
return true;
}
})
every 循环,都满足条件为true
<script>
const arr = [
{id: 1, name: '西瓜', state: true},
{id: 2, name: '榴莲', state: false},
{id: 3, name: '草莓', state: true},
];
const every = arr.every(item => item.state);
console.log(every) // false
</script>
reduce 循环
- reduce 参数1:累加的数量,默认是0,后面设置的
- 参数2,循环的每一个列
const arr = [
{id: 1, name: '西瓜', state: true, price: 10, count: 1},
{id: 2, name: '榴莲', state: false, price: 80, count: 2},
{id: 3, name: '草莓', state: true, price: 20, count: 3},
];
const reduce = arr.filter(item => item.state).reduce((amt, item) => {
return amt += item.price * item.count
}, 0);
// 简化
const reduce1 = arr.filter(item => item.state).reduce((amt, item) => amt + item.price * item.count, 0);
console.log(reduce1)
两个数组合并,将this.list数组放前面,res数组放后面合并为新数组
this.list = [...this.list, ...res]
常见问题
- 分析父子组件传值,封装成对象传递和单传递的优缺点
绑定对象就形成强绑定了,不灵活了。不是很建议,建议单个传递
- 点击第三个组件,选中的还是第一个组件解决方案?
<input type="checkbox" class="custom-control-input" id="cb1" :checked="state"/>
<label class="custom-control-label" for="cb1">
<!-- 商品的缩略图 -->
<img :src="pic" alt=""/>
</label>
那是因为上面的input和label标签的id和for,绑定的永远是一个名称
@change 发生改变触发函数
@change="changeStatus"
动态组件(is)
动态切换组件的显示与隐藏
<!-- 动态渲染组件 用is指定渲染的组件 -->
<component :is="comName"></component>
动态切换组件导致数据丢失怎么解决?
因为生命周期问题,当我们切换组件时会销毁并初始化一个组件所以数据丢失
创建不被销毁的动态组件,被括起来的组件会一直被缓存
生命周期,缓存&激活(监听谁写给谁)
当组件被缓存时,会自动触发组件的 deactivated 生命周期函数。
当组件被激活时,会自动触发组件的 activated 生命周期函数。
<!-- keep alive包含住的组件不被销毁 -->
<keep-alive>
<component :is="comName"></component>
</keep-alive>
export default {
deactivated() {
console.log("组件被缓存了")
},
activated() {
console.log("组件被激活了")
}
}
指定缓存的组件(include)
include 指定缓存的组件,或者exclude指定哪些组件不被缓存,无法同时使用
<keep-alive include="Left,Right">
<component :is="comName"></component>
</keep-alive>
给组件起名
所以名称要保持一致,建议给组件都设置名称
- 主要应用场景:keepalive组件缓存,调试
export default {
// 起名,在调试的时候就会显示该名称,在一些指定名称中的属性,也需要被更改
name: 'MyLeft',
插槽(slot)
就是在引用组件时,组件中有部分并不确定,希望灵活使用,传入什么,渲染什么
App 主组件
<Left>
<p>这是用户声明的P标签</p>
</Left>
Left 子组件
<template>
<div class="left-container">
<h3>Left 组件</h3>
<slot></slot>
</div>
</template>
- 属性name:默认default,规范是每一个插槽都要有name名称
- 默认情况下,使用的组件默认进入default插槽中
如何指定内容到指定插槽中
- v-slot:必须用在template标签上
- 使用v-slot 指定
- template是虚拟标签,不会被渲染到页面上,如果不是组件才需要包,是则不需要
- v-slot 简写是#号
<Left>
<template v-slot:default>
<p>这是用户声明的P标签</p>
</template>
</Left>
设置默认内容,如果用户指定了会被覆盖(官方:后备内容)
<slot name="default">
<!-- 在这里面写就可以 -->
</slot>
插槽的使用
插槽定义
<template>
<div class="artclue-container">
<div class="header">
<slot name="header"></slot>
</div>
<div class="content">
<slot name="content"></slot>
</div>
<div class="footer">
<slot name="footer"></slot>
</div>
</div>
</template>
插槽使用
<Artclue>
<template #header>
<p>头部是这里</p>
</template>
<template #content>
<p>这里是内容</p>
</template>
<template #footer>
<p>末尾啦</p>
</template>
</Artclue>
插槽传值
没有值就是空对象,会的到自己定义的数据(官方:叫做作用域插槽)
- scope:标准
<div class="footer">
<slot name="footer" msg="hello"></slot>
</div>
<template #footer="scope">
<p>末尾啦</p>
<p>{{ obj }}</p>
</template>
自定义指令
使用时加上v-这是固定写法
<h1 v-color>App 根组件</h1>
<p v-color="'red'">猜猜我是谁</p>
export default {
directives: {
// color 指令,当刚绑定的时候,出发bind函数
color: {
// 触发bind指令. 形参el表示被绑定的dom元素
bind(el){
console.log("已经触发" + el)
el.style.color = 'red'
}
}
}
}
- v-color 自己定义的指令
- color 这是个自定义的变量
对象中两个重要的:expression和value,前者是变量名称,后者是变量值
<p v-color="color">头部是这里</p>
update函数
由于bind函数只在被绑定的时候执行一次,不会更新内容
update函数当每次dom元素被改变,都会执行一次
export default {
data() {
return {
color: 'red'
}
},
directives: {
// color 指令,当刚绑定的时候,出发bind函数
color: {
// 触发bind指令. 形参el表示被绑定的dom元素
bind(el, binding) {
console.log(binding)
el.style.color = binding.value
},
update(el, binding) {
// console.log(binding)
el.style.color = binding.value
}
}
}
}
简化以上,当bind和update中的逻辑一致,可以直接简写一下方式
export default {
directives: {
// color 指令,当刚绑定的时候,出发bind函数
color(el, binding) {
console.log(binding)
el.style.color = binding.value
}
}
}
全局自定义指令
只要是全局指令都需要在main.js中定义
// 原始用法
Vue.directive('color', {
bind(el, binding) {
el.style.color = binding.value
},
update(el, binding) {
el.style.color = binding.value
}
})
// 简化
Vue.directive('color', function (el, binding) {
el.style.color = binding.value
})
main.js 小提示
- true:发布式要打开生产模式,起到提示作用
- false:关闭,也就是不提示
Vue.config.productionTip = true
优化axios
下面这个每个模块都需要导入,所以很麻烦
<script>
import axios from 'axios'
export default {
methods: {
async getInfo () {
const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks')
console.log(res)
}
}
}
</script>
因为组件之间都需要导包,所以可以在main.js中导入
在main.js中
// 交给vue原型
Vue.prototype.$http = axios
使用
export default {
methods: {
async getInfo () {
const { data: res } = await this.$http.get('http://www.liulongbin.top:3006/api/getbooks')
console.log(res)
}
}
}
发现这样端口这块重复太多了
定义根路径
// 交给vue原型,定义根路径
axios.defaults.baseURL = 'http://www.liulongbin.top:3006'
Vue.prototype.$http = axios
- 缺点:不利于复用
封装axios
假设有三个服务器,那就封装三个axios,实现复用
- 在src下创建utils文件夹,下的request.js文件
import axios from 'axios'
const request = axios.create({
// 指定请求的根路径
baseURL: ''
})
export default request
封装接口api模块,建立api 模块,为每个需求,建立不同的js文件(下面实例)
这样就可以直接使用了
import request from '@/utils/request'
export const getArticleList = function (_page, _limit) {
return request.get('/articles', {
params: {
_page,
_limit
}
})
}
就可以直接调用了
methods: {
async initList () {
const { data: res } = await getArticleList(this.page, this.limit)
console.log(res)
}
}
delete请求
export function delSysUser(params) {
return request.delete('/index/delSysUser', {
params: {
id: params.id
}
})
}
配置拦截器加cookie
在request中使用
const request = axios.create({
baseURL: 'http://localhost:82'
})
request.interceptors.request.use(
config => {
config.headers.token = `Bearer ${localStorage.getItem('token')}`
return config
}
)
路由(权限控制)
路由就是 hash地址与组件之间的关系(锚链接不会导致页面刷新)
前端路由工作方式
- 用户点击了页面上的路由链接
- 导致了 URL 地址栏中的 Hash 值发生了变化
- 前端路由监听了到 Hash 地址的变化
- 前端路由把当前 Hash 地址对应的组件渲染到浏览器中
结论:前端路由,指的是 Hash 地址与组件之间的对应关系!
简易路由
- window.onhashchange:监听地址栏变化
- location.hash:获取变化的地址
<template>
<div class="app-container">
<h1>App 根组件</h1>
<a href="#/Home">首页</a>
<a href="#/Move">电影</a>
<a href="#/About">关于</a>
<hr/>
<component :is="Name"></component>
</div>
</template>
<script>
// 导入组件
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'
export default {
name: 'App',
// 注册组件
components: {
Home,
Movie,
About
},
data() {
return {
Name: 'Home'
}
},
// 只要app一创建,就监听window对象的onhashchange事件
created() {
window.onhashchange = () => {
// console.log("监听到的hash地址变化" + location.hash)
let hash = location.hash;
switch (hash) {
case '#/Home':
this.Name = 'Home';
break
case '#/Move':
this.Name = 'Movie';
break
case '#/About':
this.Name = 'About';
break
}
}
}
}
</script>
<style lang="less" scoped>
.app-container {
background-color: #efefef;
overflow: hidden;
margin: 10px;
padding: 15px;
> a {
margin-right: 10px;
}
}
</style>
vue 路由的安装与配置
只能在 vue 项目中使用
- 安装 vue 路由
npm i vue-router@3.5.2 -S
- 创建路由模块,在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码
// 导入Vue和VueRouter的包
import Vue from "vue";
import VueRouter from "vue-router";
// 调用vue.use函数,把VueRouter安装为vue插件
Vue.use(VueRouter)
// 创建路由的实例对象
const router = new VueRouter();
// 向外共享路由实例对象
export default router
- 打开main.js 建立main.js和router下的js文件建立到一起,简称挂载
// 这有一个细节点,下面用的是完整路径
// 如果给到的不是具体的文件,则默认导入文件夹下index.js的文件,基于模块化
import router from "@/router/index";
new Vue({
// 因为变量名和值都是一样的,可以简写,挂载vue实例对象
router
})
vue路由的基本使用
<!-- 不建议使用a链接,建议用下面的方式 -->
<router-link to="/home">首页</router-link>
<a href="#/home">首页</a>
<a href="#/move">电影</a>
<a href="#/about">关于</a>
<!--安装了VueRouter就可以使用这个组件-->
<!-- 表示占位符,等待被渲染 -->
<router-view></router-view>
配置路由与hash的关系----在router/index关系中设置
import Home from "@/components/Home";
import Movie from "@/components/Movie";
import about from "@/components/About";
// 创建路由的实例对象
const router = new VueRouter({
// 下面叫路由规则
routes: [
// 表示空页面将会跳转到home,这里必须是路径,组件会无效
{path: "/", redirect: '/home'},
// path:表示hash地址,component表示组件
{path: "/home", component: Home},
{path: '/movie', component: Movie},
{path: '/about', component: about}
]
});
路由嵌套
也就是继续在子组件中,继续写子组件路由
// 创建路由的实例对象
const router = new VueRouter({
routes: [
// 表示空页面将会跳转到home
{path: "/", redirect: '/home'},
// path:表示hash地址,component表示组件
{path: "/home", component: Home},
{path: '/movie', component: Movie},
{
path: '/about', component: about,
// 设置默认子路由
redirect: '/about/tab1',
// 定义子路由
children: [
{path: 'tab1', component: Tab1},
{path: 'tab2', component: Tab2},
]
}
]
});
默认子路由还可以除了redirect,还可以使用以下方式
// 定义子路由
children: [
// 数组中,空字符串路径表示默认路由
{path: '', component: tab1},
{path: 'tab1', component: Tab1},
{path: 'tab2', component: Tab2},
]
动态路由
就是把hash地址中可变的部分定义为参数项,提高路由可复用性
后面定义为可变参数
<!--后面是路径参数,通过$route.params访问-->
<router-link to="/movie/3">洛基</router-link>
<router-link to="/movie/1">雷神</router-link>
<router-link to="/movie/2">妇联</router-link>
路由配置为:id,名字随意起
{path: '/movie/:id', component: Movie},
拿到router-link to参数,在路由中定义
{{ $route.params.id }
方式二
开启props 传参
{path: '/movie/:id', component: Movie, props: true},
这个id要和上面对应,写的data() 中,然后就可以直接使用id了
props: ['id'],
获取查询参数,上面是路径参数
this.$route.query.
注意:在this.$route中path只是路径不包含查询参数,fullPath是完整地址
制作导航
- 在浏览器中,点击a链接实现导航,叫做声明式导航,vue也是
- 调用api方式实现导航叫做编程式导航,就是location.href属于编程式
this.$router 是导航对象,this.$route 是参数对象
编程式跳转方式
- push方法特点:会增加一条历史记录,可以回到刚刚的页面
- replace:不会产生历史记录
- go:后退一步或者前进一步(-1,1)
- 后退:this.$router.back()
- 前进:this.$router.forward()
export default {
name: 'Home',
methods: {
gotoLk() {
// hash 地址,产生历史记录
this.$router.push("/movie/1")
// hash 地址,不产生历史记录
this.$router.replace("/movie/1")
// 有历史记录才会跳转,如果超过上限会原地不动
this.$router.go(-1)
}
}
}
导航守卫
全局前置守卫:每次路由跳转时,都会触发全局前置守卫。
如果只是声明了对象,什么也没写,表示不会进行跳转
- from:将要离开的路由对象
- to:将要访问的路由对象
- next:表示放行
//为router对象,声明全局前置导航守卫
//只要发生了路由的跳转,必然会触发 beforeEach 指定的function回调函数
router.beforeEach(function (to, from, next) {
// from 将要离开的路由对象
console.log(from);
// to 将要访问的路由对象
console.log(to);
// next 表示放行
next()
})
next 的三种跳转方式
- 当用户拥有后台主页的访问权限,直接放行:next()
- 当前用户没有后台主页的访问权限,强制跳转到登录页面:next('/login') 括号里是hash地址
- 当前用户没有后台主页的访问权限,不允许跳转后台主页,next(false)
- to.path: 将要访问的路径
判断是否登录案例
//为router对象,声明全局前置导航守卫
//只要发生了路由的跳转,必然会触发 beforeEach 指定的function回调函数
router.beforeEach(function (to, from, next) {
// 拿到用户要访问的hash地址
// 如果是main,则需要登录,否则不需要(直接放行)
// 如果是main,则拿到token值,如果有则放行,没有跳到登录页面
if(to.path === '/main'){
// 获取页面上的cookie
let item = localStorage.getItem('token');
if(item) next()
else next('/login')
}else {
next()
}
})
获取页面上cookie
// 获取页面上的cookie
let item = localStorage.getItem('token');
token 认证
规范:必须以Bearer 开头
配置vant移动端组件库
安装vant
# Vue 3 项目,安装最新版 Vant:
npm i vant -S
# Vue 2 项目,安装 Vant 2:
npm i vant@latest-v2 -S
导入want:使用方式三一次性导入所有vant组件,上线时可抽取掉vant。(最好)
在 main.js 中导入
import Vant from 'vant'
import 'vant/lib/index.css'
Vue.use(Vant)
接下来就可以直接使用vant组件了
直接使用标签语法即可
按需引入 want
依赖
npm i babel-plugin-import -D
npm i vant@latest-v2 -S
在 babel.config.js 中
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
// 添加到这里即可
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
}
创建 JS 文件,在main.js 中 引入该组件,在次js文件中引入组件
移动端适配 vh vh
依赖
npm i postcss-px-to-viewport -S
在根目录创建 postcss.config.js 文件
// postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport': {
// vw 适配标准屏的宽度,设计图的一倍图,比例:设计图750 适配 375
viewportWidth: 375
}
}
}
导航栏组件库
导航栏路由
<!-- 预留空间-->
<router-view></router-view>
<!-- 定制路由 -->
<van-tabbar route>
<van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
<van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
</van-tabbar>
固定区域,上导航栏,返回前进按钮
<van-nav-bar
title="标题"
fixed // 固定住,默认false
left-text="返回"
right-text="按钮"
left-arrow // 左侧箭头是否显示
@click-left="onClickLeft"
@click-right="onClickRight"
/>
覆盖第三方样式的时候不生效,就要加/deep/
下拉更新数据,中间放需要更新的数据
- loading:是否需要更新,为true不会反复触发load事件 // 声明 true,create函数触发后要改为false
- @load:更新函数 // 创建函数
- finished:是否加载了所有数据,没有应该为false // 声明 false
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
</van-list>
我们应该往上滑才触发,刚进来不需要触发,所以需要将loading改为false
实现流程
- 渲染的组件
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<article-info v-for="item in list" :key="item.id" :title="item.title" :aut-name="item.aut_name"
:comm-count="item.comm_count" :pubdate="item.pubdate" :cover="item.cover"></article-info>
</van-list>
- js
export default {
name: 'Home',
components: { ArticleInfo },
data () {
return {
list: [], // 数据容器
loading: true, // 数据是否反复家长,true不加载
finished: false, // 数据是否全部加载完毕
page: 1,
limit: 10
}
},
created () {
this.initList()
},
methods: {
async initList () {
const { data: res } = await getArticleList(this.page, this.limit)
// 合并两个数组
this.list = [...this.list, ...res]
// 加载完改false
this.loading = false
// 判断是否全部加载完毕
if (res.length === 0) this.finished = true
},
// 加载数据
onLoad () {
this.page++
this.initList()
}
}
}
</script>
下拉追加数据,应该把第一页的数据重置为下一页的数据,也就是在数组前面加
- disabled 表示没有最新数据了
<van-pull-refresh v-model="refreshing" :disabled="finished" @refresh="onRefresh">
export default {
name: 'Home',
components: { ArticleInfo },
data () {
return {
list: [],
loading: true,
finished: false,
refreshing: false, // 是否正在下拉刷新
page: 1,
limit: 10
}
},
created () {
this.initList()
},
methods: {
async initList (isRefresh) {
const { data: res } = await getArticleList(this.page, this.limit)‘
// 如果数据是下拉刷新,就进入下面,
if (isRefresh) {
// 将新数据放前面
this.list = [...res, ...this.list]
// 数据更新完毕关闭刷新
this.refreshing = false
} else {
this.list = [...this.list, ...res]
this.loading = false
}
if (res.length === 0) this.finished = true
},
onLoad () {
this.page++
this.initList()
},
onRefresh () {
this.page++
this.initList(true)
}
}
}
</script>
定制主题
- 先把引入的index.css改成index.less
- 根目录创建vue.config
- 里面的样式表,是在vant需要的组件中查找
优缺点:需要重启,才能生效,不建议使用
module.exports = {
css: {
loaderOptions: {
less: {
// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
lessOptions: {
modifyVars: {
// 直接覆盖变量
'text-color': '#111',
'border-color': '#eee'
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)
// hack: 'true; @import "your-less-file-path.less";',
}
}
}
}
}
}
less定制主题
- 根目录创建vue.config,里面写
const path = require('path')
// 获取文件的绝对路径
const themePath = path.join(__dirname, '/src/theme.less')
// vue.config.js
module.exports = {
css: {
loaderOptions: {
less: {
modifyVars: {
// 或者可以通过 less 文件覆盖(文件路径为绝对路径),赋值绝对路径
hack: `true; @import "${themePath}";`
}
}
}
}
}
- 新建less
- 填写变量
@blue: #007bff;
// 下面的类名是vant提供的
@nav-bar-background-color: @blue;
默认打包后无法通过file协议进行打开
因为只支持http协议不支持file协议需要修改配置
module.exports = {
// 改为空就可以了
publicPath: '',
lodash中文文档可以提供了很多实用的api
修改默认端口号
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 7000
}
})
Element ui 组件库
安装element UI
npm i element-ui -S
在 main.js 中引入组件
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
可以直接复制代码使用了
分页
<!-- 分页 -->
<el-pagination
background
:current-page="formInline.page" // 当前页码
:page-size="formInline.limit" // 当前每页数量
:page-sizes="[10, 20, 30, 40, 50, 100]" // 切换每页的数量
layout="prev, pager, next, total, jumper, sizes"
@size-change="handleSizeChange" // 每页数据
@current-change="handleCurrentChange" // 页面显示数据
:total="1000">
</el-pagination>
methods: {
// 切换每一页多少数据
handleSizeChange (val) {
console.log(val)
},
// 跳转页面
handleCurrentChange (val) {
console.log(val)
}
}
利用插槽获取这一行的数据---row 就是这一行的数据对象
<template #default="scope">
{{ scope.row.gender === 1 ? '男' : '女' }}
</template>
表单校验
校验规则
loginRules: {
username: [{
required: true,
trigger: 'blur',
message: '请输入您的账号'
}],
password: [{
required: true,
trigger: 'blur',
message: '请输入您的密码'
}]
}
提交时校验
handleLogin (loginForm) {
// loginForm 这里的名字要和ref后面的名字一样
this.$refs[loginForm].validate(async (valid) => {
if (valid) {
const { data: res } = await Login(this.loginForm)
console.log(res)
} else {
return false
}
})
}