Vue
- 国产渐进式javaScript框架
- 易用:熟悉HTML CSS javaScript可快速上手
- 灵活:在一个库和一套完整的框架之间自如伸缩
- 高效:20KB运行大小,超快虚拟DOM(渲染页面速度超快)
- 运行原理:Vue代码>Vue框架解析>js原生代码
Vue基本语法
- MVVM设计思想
- M模型(model) Data
- V视图(view) DOM
- VM视图模型 new Vue({}) 实现控制逻辑
Vue实例化对象的属性
- new Vue({}) 内的this指向它的实例对象vm (Vue框架内部对this进行了处理)
- {{数据}} 插值表达式 (将数据填充到HTML中 可以进行一些简单的运算)
- el : 元素的挂载位置 (可以是css选择器也可以是DOM元素)
- data: 模板数据
- methods : 专门用于存放事件的方法 v-on执行的值直接调用这里面的方法
- directives: 局部自定义指令创建
- 只能在该实例(组件)中使用
- computed:计算属性 设置插值表达式中函数 不需要用()调用
- watch: 侦听器属性 当监听的数据发生变化时触发
- filters: 局部过滤器创建 对调用的数据进行函数内的逻辑处理
- 只能在该实例(组件)中使用
//引入vue文件
<script src="./vue.js"></script>
<div id="app">
<h2>{{msg}}</h2>
</div>
<script>
var vm = new Vue({
el:'#app',
data:{
msg:'hellow vue',
num:0
}
methods:{
handle:function(){
this.num++;
}
},
directives:{
focus:{
//el指当前使用这个指令的DOM元素
inserted:function(el){
el.focus()
}
}
},
computed:{
reverseString:function() {
//这里的this指当前vue实例对象
return:this.msg.split('')
}
},
watch:{
msg:function(val) {
console.log(val)
}
},
filters:{
过滤器名称:function(val) {
return value.charAt(0)
}
}
})
</script>
模板语法
- 模板+数据 = 前端渲染(静态HTML内容)
- 比起前端模板引擎多出了更多的模板语法
指令v-text v-html
- 指令的本质就是自定义属性
- Vue指令的格式以v-开始
-
v-cloak(了解)
- 插值表达式是需要解析完才会将解析完的代码显示在页面上,如果刷新过慢会导致用户先看到插值表达式
- 解决方案:先隐藏,替换好值再隐藏
//1.提供样式 <style> [v-clock]{ display:none } </style> //在插值表达式所在标签添加v-clock指令(也就是自定义属性) <div v-clock>{{msg}}</div>
-
v-text 填充纯文本(可以解决’闪动’问题 比v-cloak更方便)
<div v-text="msg"></div>
-
v-html 比起v-text 可以解析html标签(存在安全隐患)
- 存在安全隐患(xss攻击),本网站数据可以使用 跨域(非同源) 不可使用
- xss攻击:javascript脚本攻击,因为会解析script标签 可能会被人恶意传输死循环数据等
<div v-html="msg1">{{msg}}</div>
-
v-pre 显示原始信息 跳过编译(原文输出)
<div v-pre>{{msg}}</div> // html显示{{msg}}
数据响应式v-noce
-
数据的响应式:数据的变化会影响页面变化
-
数据绑定:插值表达式和data数据就是绑定的关系 data更改 插值表达式就会变动
-
v-once 只编译一次 显示内容之后就不再具有响应式功能(系统就不再监听这个数据,提高了性能)
<div v-noce>{{msg1}}</div>
双向数据绑定v-model
-
数据的变化会影响页面,而页面的变动不会改变数据的变化,这是单向的
-
双向:模型(数据)的变化视图(页面)会变化,视图(页面)变化模型(数据)也会变化
-
v-model="" 引号中填data input的val变化 这个数据的值也会跟着变化
<input type="text" v-model="data"/>
事件绑定v-on
- 指令用法 v-on:事件类型=“事件处理”
- 简写方式 @事件类型=“事件处理”
//可以逻辑运算
<input type="button" @click="num++"/>
//也可以传递函数 这个函数是new Vue内设置
<input type="button" v-on:click="handle()"/>
事件函数参数传递
//事件绑定函数名称,默认传递一个事件函数
<input type="button" @click="handle"/>
//事件绑定函数调用,参数传递多个 要传递事件函数$event
<input type="button" @click="handle(1,2,$event)"/>
事件修饰符 stop prevent
//阻止事件冒泡 v-on:click.stop
<a href="http://www.baidu.com" v-on:click.stop="fn()">跳转</a>
//阻止事件默认行为 v-on:click.prevent
<a href="http://www.baidu.com" v-on:click.prevent="fn()">跳转</a>
//修饰符还可以串联 v-on:click.stop.prevent
按键修饰符 enter delete
//按下回车键时触发事件
<input v-on:keyup.enter="submit"/>
//按下退格键时触发事件
<input v-on:keyup.delete="handle"/>
自定义按键修饰符
//给112keyCode码起个 f1名字
Vue.config.keyCodes.f1 = 112;
//按下f1(keycode码为112) 触发submit事件
<input v-on:keyup.f1="submit"/>
属性绑定v-bind
//语法1 url为data对象内的属性 其值为链接
<a v-bind:href="url">百度</a>
//简写语法
<a :href="url">百度</a>
v-model底层原理
//v-bind 将模型影响视图
//v-on:input 视图改变时调用函数影响视图
<input type="text" v-bind:value="msg" v-on:input="msg=$event.target.value">
class样式处理
- 对象的属性就是类名,这个属性的值是data属性的话,它的值要设置成布尔值
- 数组的值如果是字符串,那它的值就是类名 (数组的布尔值为真)
- 数组的值如果是对象 data属性中这个对象的属性和值就是 类名对应布尔值 active:true
//active是样式名称 isActive是data的属性,值为布尔值,值为true添加这个类名 否则不添加
//可以通过调用方法更改 data属性isActive的值
//对象语法
<div v-bind:class="{active:isActive}"></div>
//数组语法 数组元素为data的属性 值为类名
<div v-bind:class="[activeClass]"></div>
//对象绑定和数组绑定可以结合使用
<div v-bind:class="[activeClass,{test:isTest}]"></div>
//简化操作
<div v-bind:class="arrClass"></div>
<div v-bind:class="objClass"></div>
data:{
arrClass:['active','error'] //data属性中设置值为数组 active和error为类名
objClass:{
active:true,
error:true
}
}
//默认的class会保留 此处div有两个class
<div class="base" v-bind:class="[activeClass]"></div>
style样式绑定
- 和class样式处理一样都可以进行对象语法,数组语法,简化的方式操作
//对象语法 对象内data属性的值为字符串样式
<div v-bind:style="{color:activeColor,fontSize:fontSize}"></div>
activeColor:'blue'
//数组语法 数组元素的data属性的值为对象 对象内属性为样式和值为样式值
<div v-bind:style="[arrStyle]"></div>
arrStyle:{
color:'red',
backgroundColor:'skyblue'
}
分支结构用法
分支循环结构if show
- 根据分号中的条件表达式渲染其中一个div 其他不在DOM结构中
- v-if是控制元素(结构)
<div v-if="num>=10">大于10</div>
<div v-else-if="num<10&&num>0">0~10之间</div>
<div v-else>小于0</div>
data:{
num:5
}
- v-show 是控制样式(display:none) DOM中有这个元素 只不过是隐藏了
<div v-show="false">大于10</div>
data:{
flag:false
}
循环结构v-for
- v-for 遍历数组
- item是数组中的当前循环元素 index是当前循环元素的索引
<ul>
<li v-for="item in fruits">{{item}}</li> //方式1 不带索引
<li v-for="(item,index)in fruits">{{item}}{{index}}</li> //方式2 带索引
</ul>
data:{
fruits:['apple','orange']
}
- v-key : 帮助vue区别不同的元素 从而提高性能
- v-for中加 :key 可以提高性能 如果数组元素不是对象没有id 填索引也可以
<li v-for="(item,index)in fruits" :key="item.id">{{item}}{{index}}</li>
data:{
fruits:[{
id:1,
ename:'apple'
},{
id:2,
ename:'orange'
}]
}
- v-for遍历对象
- v-if可以和 v-for结合使用 当v-if条件达成时才会渲染页面
//value 对象属性 key 属性值 index 索引
<li v-for="(value,key,index) in obj">{{value + "---" + key + "---" + index}}</li>
data:{
obj:{
name:'zhangsan'
age:20
}
}
声明式编程
模板的结构和最终显示的结果基本一致
vue常用特性
表单的基本操作
- input的单选框(radio) 和多选框(checkbox) 下拉框(select) 每个选项都要一个value属性来区分
- input表单的单选项 的v-model 中的属性名的设为单个值
- input表单的下拉框 的v-model 中的属性名的设为单个值
- input表单的多选项 的v-model 中的属性名的值是一个数组
- text和textarea的 v-model 中的属性名的设为单个值
- submit 通过@click.prevent阻止默认行为并触发methods中的函数
表单域修饰符 .number .trim .lazy
- number:将input输入的内容变为数字型的(默认是字符串型的)
- 如果输入的值不能转换 会变成字符串型的
- trim:将input输入的内容去除两边的空格
- lazy: 将表单默认的input事件转换为change事件 ,input失去焦点的时候触发
<input type="text" v-model.number="age"/> 输入内容的内容由
自定义指令 全局指令Vue.directive
- 使用的时候前面要加v-
- inserted:指令插入标签中的时候触发其中的函数
- bind:只调用一次
- el为当前调用的DOM元素
- 全局指令Vue.directive 可以在所有的Vue实例对象中使用
<input type="text" v-foucs />
Vue.directive('foucs',{
inserted: function(el) {
el.focus() //获取焦点方法
}
})
局部指令
- 局部指令只能在该Vue实例对象中使用
<input type="text" v-color />
var vm = new Vue({
el:'#app',
directives:{
color:function(el) {
//el指当前使用这个指令的DOM元素
el.style.backgroundColor = 'red';
}
}
})
自定义指令带参数
- el:当前使用自定义指令的DOM元素
- binding:一个对象 里面包含了一些自定义指令信息
- value属性 :值为自定义指令传递的参数msg
- name属性 :值为自定义指令名
<input type="text" v-color="msg" />
var vm = new Vue({
el:'#app',
data:{
msg:{
color:'blue'
}
},
directives:{
//el指使用当前指令的input元素
color: {
inserted:function (el, binding) {
el.style.backgroundColor = binding.value.color;
}
}
}
})
计算属性 computed
- 将一些复杂的操作封装到这个属性中 类似函数封装
- 计算属性是基于模型数据来进行处理
- 再通过插值表达式{{自定义函数名称}} 来使用
- 插值表达式调用时不要写小括号()
<div>{{reverseString}}</div>
data:{
msg:'Hellow'
},
computed:{
reverseString:function() {
//这里的this指当前vue实例对象
return:this.msg.split('')
}
}
计算属性与方法的区别
- 计算属性调用时会产生缓存,如果他所依赖的模型数据没有改变,他会从缓存中取出结果,可以节省CPU性能。
- 而方法不会进行缓存,只要调用就执行一次。
侦听器 watch
- watch 当数据监听的数据发生变化时就会调用其中的函数
- function(val)中的参数val就是监听的属性的值
data:{
msg:'';
},
watch:{
msg:function(val) {
console.log(val)
}
}
全局过滤器和局部过滤器 filter
- 全局过滤器Vue.filter
- 过滤器和自定义指令一样 有全局过滤器和局部过滤器
- 使用方式1:
{{msg | 过滤器名称}}
- 使用方式2:
//过滤器的使用
<div>{{msg | format}}</div>
//创建全局过滤器
Vue.filter('format',function(val){
return value.charAt(0)
})
- 局部过滤器filters
filters:{
过滤器名称:function(val) {
return value.charAt(0)
}
}
携带参数的过滤器
- 如果要携带多个参数可以传递一个对象过去比较好
<div>{{msg | format('yyyy-MM-dd')}}</div>
// val获得的是data中msg的值 arg获得的是format()小括号中的参数
Vue.filter('过滤器名称',function(val,arg){
if (arg == 'yyyy-MM-dd') {
return 处理业务逻辑
}
})
生命周期(Vue实例的产生过程 )
- 创建(初始化)
- beforeCreate Vue实例对象创建前调用
- created Vue实例对象创建完毕调用
- 挂载(渲染视图)
- beforeMount 在挂载开始之前调用
- mounted 挂载完毕后调用
- 更新
- beforeUpdate 数据更新时 调用
- updated 数据更新完毕后调用
- 销毁
- beforeDestroy 实例销毁之前调用
- destroyed 实例销毁完毕调用
var vm = new Vue({
el:'#app',
data:{
},
mounted:function(){
//挂载完毕后调用
},
beforeUpdate:function(){
//数据更新时 调用
},
updated:function(){
//数据更新完毕后调用
}
})
Vue数组处理相关概念
- 变异方法: push() pop() shift() unshift() splice() sort()排序 reverse()
- Vue支持影响原数组的方法 响应式数据
- 数组变化 视图会跟着变化
- 非变异方法:filter()过滤 concat()组合 slice() 切片 findIndex()查询符合条件元素索引
- 不影响原数组的方法 Vue不支持 响应式数据
- 返回一个新数组 所以需要重新赋值给原数组才能修改
数组对象响应式处理
- 不是初始化的数据,一般的DOM操作修改数组或对象的值无法做到响应式,解决方案:Vue提供给我们提供了一个set方法
- Vue.set(vm.items,indexOfItem,newValue) 修改新增的数组元素或对象属性(实现修改后响应式视图)
- 第一个参数 :实例data中的数组或对象名
- 第二个参数 : 数组的索引 或 对象的属性名
- 第三个参数 :修改后的值
- 语法2:vm.$set(vm.items,indexOfItem,newValue)
组件化
组件的注册
- data的值必须是个函数 ,每个组件的数据都是独立的 ,如果是对象 每个组件的数据都会共享.
- template:组件模板必须是单个根元素, 不能是两个兄弟标签,可以单个父级标签包裹多个兄弟标签。
- 可以使用模板字符串``
- 组件的命名方式
- 短横线 button-count 单词小写 单词之间用 - 分割
- 驼峰命名 只能在模板字符串中使用 在中使用会出错
//全局组件的注册
Vue.component(组件名称,{
data:function(){ return {组件数据} },
template:'组件模板内容',
methods:{ 组件能调用的函数}
})
//定义一个名为button-counter的新组件
Vue.component('button-counter',{
data:function() {
return {
count:0
}
},
template:'<button @click="count++">点击了 {{count}} 次</button>'
})
//使用
在页面要使用的地方插入 <button-counter></button-counter>
局部组件
- 局部组件只能在父组件中使用,id=app的盒子中, 不能在全局组件中使用 超出了作用范围.
var vm = new Vue({
el:'#app',
components:{
'component-a':{
data:function(){
return { msg:'zhangsan'}
},
template:'<div>demo<div>'
},
'component-b':{
data:function(){
return { msg:'lisi'}
},
template:'<div>demo<div>'
}
}
})
父组件向子组件传值
- 在props属性中使用驼峰命名方式,模板中就要使用短横线的形式
- 在组件使用时传递属性值,用短横线的方式传递到(父组件中调用时)
- 在组件定义和模板字符串中 用驼峰命名
//父组件的V
//parr为父组件传递的data属性的值 通过:p-arr将parr传递到props的pArr中
//这样template可以通过props内的数据就可以使用父组件传递过来的值
<div id="app">
<component-a :p-arr="parr"></component-a>
</div>
//子组件的VM
Vue.component:(
'component-a',{
//props中的值 可以在子组件template中使用
props:['pArr']
//子组件
template:'<div v-for="(item,index) in pArr" :key="index">{{item}}<div>'
},
)
//父组件的VM
var vm = new Vue({
el:'#app',
data:{
parr:['apple','orange']
}
})
子组件向父组件中传值
- Vue父子组件通讯,遵循单向数据流
- 只能父级对子级操作,子级不能直接对父级进行操作.
- 子级要向父级进行操作,需要通知父级, 然后父级接到通知 自行修改操作
//父组件的V
<div id="app">
//2.@父组件监听子组件中的enlarge-text事件 ,当他触发时调用 父组件中的handle函数
<component-a :p-arr="parr" @enlarge-text="handle"></component-a>
</div>
//子组件的VM
Vue.component:{
'component-a':{
//1.点击时触发 $emit中设置的自定义事件
template:'<buttom @click="$emit('enlarge-text')">按钮</buttom>'
},
}
//父组件的VM
var vm = new Vue({
el:'#app',
methoeds:{
handle:function() {
console.log('子级发送请求 父级监听后自行修改')
}
}
})
子组件向父组件中传值携带参数
//父组件 通过$event接收传递过来的函数并作为实参给handle函数
<component-a :p-arr="parr" @enlarge-text="handle($event)"></component-a>
//子组件 $emit方法第二个参数可以给自定义事件传递参 多个参数可以使用数组或者对象传递
template:'<buttom @click="$emit('enlarge-text',0.1)">按钮</buttom>'
兄弟组件之间的数据交互
- var hub = new Vue() 创建事件中心
- hub.$on( ‘自定义事件名’ , 事件处理函数 ) 事件监听 (mounted挂载完毕时监听)
- hub.$emit(‘触发的兄弟组件监听的事件名’ , 传递的参数) 触发事件
- hub.$off(‘删除的自定义事件名’) 销毁事件
//1.创建事件中心hub
var hub = new Vue()
Vue.component('test1',{
//data:function()可以简写为data()
data:function(){
return {
uname:"zhangsan"
}
},
//3.点击触发函数
template:`<button @click="handle1"></button>
`,
methodes:{
//4.函数中触发兄弟组件中的事件 可以将自身的一些参数传递过去
handle1:function() {
hub.$emit('event2',this.uname)
}
},
mounted:{
// 2. 挂载完毕后监听事件 ,如果触发了就调用处理函数
hub.$on('event1',(uname)=>{
console.log(uname) //输出 lisi
})
}
})
Vue.component('test2',{
data:function(){
return {
name:"lisi"
}
},
//3.点击触发函数
template:`<button @click="handle2"></button>
`,
methodes:{
//4.函数中触发兄弟组件中的事件 可以将自身的一些参数传递过去
handle2:function() {
hub.$emit('event1',this.uname)
}
},
mounted:{
// 2. 挂载完毕后监听事件 ,如果触发了就调用处理函数
hub.$on('event2',(uname)=>{
console.log(uname) // 输出 zhangsan
})
}
})
//父组件中
var em = new Vue({
methods:{
//调用该函数时 销毁事件
delete:function(){
hub.$off('event1');
hub.$off('event2');
}
}
})
插槽 slot
- 在子组件中提供一个位置存放插槽
- 父组件标签中的内容将显示在slot所在的位置
- slot中可以填默认内容,如果父组件中没有填内容,将会使用默认内容
//父组件中
<mydiv>补充到插槽位置的内容</mydiv>
//子组件中
Vue.component('mydiv',{
template:`<slot name="header">如果父组件标签中没有填充内容,会使用插槽中的内容</slot>`
})
具名插槽(用于多个插槽时)
- 子组件中的slot 加一个name属性
- 父组件的API添加slot属性值等于子组件的slot的name属性的值 即可匹配插槽
- template 插槽内使用有多个标签要使用时用
//父组件中
<mydiv>
<!--
<div slot="header">单个标签直接添加slot属性即可</div>
-->
<template slot="header">
填充到name属性值为header的插槽中,可以写多个标签,
</template>
</mydiv>
//子组件中
Vue.component('mydiv',{
template:`<slot name="header">如果父组件标签中没有填充内容,会使用插槽中的内容</slot>`
})
作用域插槽
- 父组件对子组件的内容进行加工处理
//父组件 slotProps可以获取子组件插槽中的所有属性的集合(是一个对象)
<mydiv>
<template slot-scope="slotProps">
//当前循环的是id为2的数据时 添加高亮效果
<strong v-if="slotProps.info.id==2" style="{color:blue}">{{slotProps.info.name}} </strong>
</template>
</mydiv>
//子组件
Vue.component('mydiv',{
//此处省略一个if for循环
<slot :info='item'>{{item.name}}</slot>
})
- slot-scope可以接收当前作用域的数据
异步进程回调地狱和Promise解决方案
- 多层重复的异步进程进行嵌套,就叫回调地狱.
- Promise本身是一个构造函数,要使用promise解决回调地狱的问题 需要使用new运算符创建Promise构造函数的实例对象
- 在创建对象的时候需要传入一个匿名函数,匿名函数中有两个参数 resolve,reject
- Promise实例对象方法:
- then()方法 ,用于调用resolve函数
- catch()方法也有个方法用于调用reject函数
- finally()方法 成功与否都会执行(非标准)
- 如果返回值是个普通值,自动转换成promise对象,并作为新promise对象的resolv
function p1() {
return new Promise((resolve,reject) => {
fs.readFile('./1.txt','utf8',(err.data) => {
resolve(data)
})
})
}
function p2() {
return new Promise((resolve,reject) => {
fs.readFile('./2.txt','utf8',(err.data) => { //当文件读取的时候
resolve(data) //
})
})
}
p1().then((r1) => { //先调用p1函数
console.log(r1) //r1就是接收Promise的 函数resolve(data) 内的返回值data
return p2(); //当第一个异步API读取完毕后 再返回包裹第二个异步API的函数调用
})
.then((r2) => { //上一个函数返回了的调用链式链接了这个 可以达到依次执行的效果
console.log(r2) //r2就是接收Promise的 函数resolve(data) 内的返回值data
})
promise对象方法
- all 同时处理多个异步任务,所有任务都完成才能得到返回结果
- rece 同时处理多个异步任务,只要有一个完成就返回结果,
promise.all([p1,p2,p3]).then(function(result){
console.log(result)
})
promise.race([p1,p2,p3]).then(function(result){
console.log(result)
})
Fetch
- 更加简单的数据获取方式,功能更强大更灵活,可以看做xhr的升级版
- 基于promise实现
fetch(url).then(data=>{
//text属于fetch的一部分
return data.text();
}).then(res=>{
//res获得的才是服务器响应的实际数据
console.log(res)
})
Fetch的get和delete请求参数
- Restful的形式传参需要在路径后面带请求参数
fetch(url,{
method:'get'
}).then(data=>{
//text属于fetch的一部分
return data.text();
}).then(res=>{
//res获得的才是服务器响应的实际数据
console.log(res)
})
Fetch的post和put请求参数
//json
fetch(url/请求参数,{
method:'put',
body:JSON.stringify({
uname:'lisi',
pwd:'123'
}),
headers:{
'Content-Type':'application/json'
}
})
//普通
fetch(url,{
method:'post',
body:'uname=lisi&pwd=123',
headers:{
'Content-Type':'application/x-www-form-urlencoded'
}
}).then(data=>{
//text属于fetch的一部分
return data.text();
//响应json 数据用 data.json()
}).then(res=>{
//res获得的才是服务器响应的实际数据
console.log(res)
//响应json数据 需要res进行处理 console.log(JSON.parse(res))
})
axios的基本用法
- 引入axios库文件
- res 获取返回的一系列属性 .data获得服务器返回的内容
axios.get('请求路径')
.then(function(res){
console.log(res.data)
})
axios的get和delete请求传参(delete方式和get基本一致)
- 传统路径传参 (‘请求路径/传递参数’)
- Restful路径传参 get(‘请求路径/:id’)
- params属性传参
//路径传参
axios.get('请求路径/传递参数')
.then(function(res){
console.log(res.data)
})
//params属性传参
axios.get('请求路径',{
params: {
id:123
}
})
.then(function(res){
console.log(res.data)
})
axios的post和put请求参数传递(put方式和pots基本一致)
- json参数传递 自动转换为json格式参数
- URLSearchParams 传递参数 自动转换为 &分割的 路径表单格式 参数
//json参数传递
axios.post('请求路径/:传递参数',{
uname:'zhangsan',
pwd:'123123'
})
.then(function(res){
console.log(res.data)
})
//通过URLSearchParams 传递参数(application/x-www-form-urlencoded)
const params = new URLSearchParams()
params.append('uname','zhangsan')
params.append('pwd','123123')
axios.post('请求路径',params)
.then(function(res){
console.log(res.data)
})
axios响应结果的属性 与全局配置
- data : 服务器返回的实际数据
- status 状态码
- headers 响应头信息
- statusText 状态码信息
axios.defaults.timeout = 3000; // 超时时间
axios.defaults.baseURL = 'http://localhost:3000/'; //默认地址
axios.defaults.headers['mytoken'] = ''; //设置请求头
axios拦截器
- 请求拦截器 在发出请求之前配置一些信息
- 思考:只拦截以什么什么路径开头的请求
axios.interceptors.request.use(function(config){
return config;
},function(err) {
//处理响应的错误信息
})
- 响应拦截器 最先接收到响应结果 并可以配置一些信息
axios.interceptors.response.use(function(res){
result res;
},function(err) {
//处理响应的错误信息
})
async await 函数
async function() {
const res = await axios.get('请求路径');
return res;
}
console.log(res)
路由(资源列表)
- 后端渲染(存在性能问题,不断刷新页面)
- Ajax前端渲染(提高了性能,但不记录历史,无法前进后退)
- SPA:基于URL的hash哈希值变化(不会触发新的url请求,并且能记录历史) 来触发Ajax改变页面的内容.
- 前端路由:根据不同的用户事件,显示不同的内容,是实现SPA的核心技术 (用户事件与事件处理函数之间的对应关系)
根据哈希值hash(锚点#)实现简易的前端路由
- 哈希值以#开头
- 通过点击触发哈希值的变化, 监听事件中对哈希值的变化进行不同的页面内容显示
//监听哈希值的变化
window.onhashchange = function() {
//获取当前网址中的哈希值#/zhuye
location.hash.slice(1)
}
Vue.Router的基本使用
-
引入 Vue 库文件 Vue.Router库文件
-
添加router-link路由链接
<!-- router-link是Vue中提供的标签 默认为a标签 to属性默认渲染为href属性 属性值默认渲染为#开头的hash地址 --> <router-link to="/user">User</router-link>
-
添加路由占位符 (组件显示的位置)
- or
- 通过路由规则匹配到的组件会渲染到占位符所在的位置
-
创建路由组件 (填充到占位符的内容)
var User = { template:`<div>User</div>` } var Register = { template:`<div>Register</div>` }
-
创建路由实例对象并设定规则
//VueRouter是 库文件中的构造函数 var router = new VueRouter({ //所有路由的规则 routes:[ //访问该hash地址时 占位符显示该对象内的组件 {path:'/user'.component:组件名称} ] })
-
挂载路由实例对象
var vm = new Vue({ el:'#app', router:router })
路由重定向redirect
- redirect指定一个新的地址 ,实现重定向 (在规则中设置)
var router = new VueRouter({
//所有路由的规则
routes:[
//redirect重定向地址(一般用于设置默认显示页面)
{path:'/',redirect:'/user'}
{path:'/user'.component:组件名称}
]
})
路由嵌套 父级包含子级
- 通过children 属性创建子路由规则
//配置嵌套组件
var Register = {
template:`<h1>Register</h1>
<hr />
<router-link to="/register/Tab1">tab1</router-link>
<router-link to="/register/Tab2">tab2</router-link>
<router-view/>`
}
var tab1 = {
template:`<h3>子组件 tab1 </h3>`
}
var tab2 = {
template:`<h3>子组件 tab2 </h3>`
}
//配置路由规则
var router = new VueRouter({
routes:[
//redirect重定向地址
{path:'/',redirect:'/user'}
{path:'/user'.component:组件名称}
//children 子路由规则也是一个规则
{path:'/register',component:register,children:[
{path:'/register',redirect:'/register/Tab1'}
{path:'/register/Tab1',component: Tab1}
{path:'/register/Tab2',component: Tab2}
]}
]
})
动态路由匹配
- 用于显示同样的页面 但是可以传递不同的id路径参数
- 组件模板中可以通过$route.params获得路由参数
- props属性值类型 (组件中通过props属性配置传递过来的对象属性名在模板中使用)
- 值设为布尔值true 传递动态参数 route.params会被设置成组件属性
- 值设为对象 传递静态参数
- 值传递一个函数 返回值为一个对象 传递动态+静态参数
//路由规则设置了:id /user/ 后面的路径会被当做参数 不同的参数也会显示同样的组件
<router-link to="/user/1">User1</router-link>
<router-link to="/user/2">User2</router-link>
const User = {
//对应路由规则中的props属性中的
props:['name','age','id'],
template:`<div>{{id}}}</div>
`
}
var router = new VueRouter({
routes:[
{
path:'/user/:id',
component:User,
props:route=>({
name:'zs',
age:20,
id:route.params.id
})}
]
})
命名路由
- 可以给某个路由规则起一个别名,用于表示路由的路径
//routes路由规则中某一个规则添加user属性
{path:'/user/:id',name:'user',component:User}
//路由链接中 params传递路由参数
<router-link :to="{name:'user',params:{id:123}}">User</router-link>
编程式导航(常用于控制路由跳转)
- 不同于a链接是html跳转 用js跳转就是编程式导航
//通过点击触发函数 使用编程式导航控制跳转页面
this.$router.push('/register')
//带查询参数 路径变为 /register?uname=zs
this.$router.push({path:'/register',query:{uname:'zs'}})
//在历史记录中前进1和后退-1 刷新0
this.$router.go(1)
// 获取地址栏中的请求路径 例:/home
this.$route.path
模块化开发
- 解决了命名冲突,文件依赖等问题,方便代码重用,便于维护.
- 模块与模块之间相互隔离,需要特定了接口导入其他模块暴露的成员才能进行模块之间的交互.
- 模块成员导出: module.exports 和exports
- 默认导出只能使用一次
- 模块成员导入: require(‘模块标识符’) 如果没有成员导入 接收一个空对象{}
- 导入文件 import
//m1.js 暴露成员
export default {
a,
b,
show
}
//import 导入成员
import m1 form './mi.js'
按需导出和按需导入
- 按需导出可以使用多次
//m1.js 按需导出,
export let c = 1;
export let d = 2;
//index.js中 import按需导入
import m1,{ c , d} form './mi.js'
console.log(c,d)
//用as给导入的c起别名 c1
import { c as c1} form './mi.js'
直接导入并执行
//m2.js 中
console.log('直接导入')
//index.js中 直接执行模块中的代码
import './m2.js'
webpack(打包工具)
- 前端项目构建工具
- 提供了友好的模块化开发支持,具有代码混淆,处理兼容性问题,性能优化等功能.
webpack基本使用
- 使用npm init -y 安装项目描述文件
-
安装 : npm install webpack webpack-cli -D //-D 放入开发依赖中
-
在项目根目录中创建 webpack.config.js 的配置文件
module.exports = { mode:'development'
}
3. 在package.json配置文件中scripts节点下 新增sev脚本
```js
"scripts":{
"dev":"webpack" //节点下的脚本 可以通过 npm run执行
}
-
在终端运行 npm run dev命令 启动 webpack 进行项目打包
-
将script文件路径 改为打包好的文件路径
//dist为webpack帮我们创建的文件夹 <script src="../dist/main.js"></script>
webpack的配置
- mode :指定构建模式(打包模式)
- development : 项目开发模式(项目依赖) 不会进行压缩 速度更快可以进行代码查看
- production : 产品发布模式(开发依赖) 会进行压缩和混淆
- entry : 设置入口文件 (要打包的文件,使用绝对路径)
- output : //设置文件输出路径和文件名 (打包好的文件,使用绝对路径)
- path : 设置输出路径目录
- filename : 设置输出后的文件名
- plugins : 插件列表
//引入系统路径模块
const path = require('path')
module.exports = {
// 指定构建模式
mode:'development',
//设置入口文件(要打包的文件)
entry:path.join(__dirname,'./src/index.js'),
//设置文件输出路径和 文件名(打包好的文件)
output:{
path:path.join(__dirname,'./dist'),
filename:'bundle.js',
},
plugins:[htmlPlugin]
}
webpack自动打包
- 自动打包安装: npm i webpack-dev-server -D
- 打开package.json 更改 script配置 “dev” : “webpack-dev-server”
- 将src>index.html中 script标签引用路径 改为 “/bundle.js”
- 运行 npm run dev 命令重新打包 (类似NodeJS的nodemon app.js)
- 在服务器中通过 http://localhost:8080地址 查看打包效果
- webpack-dev-server 会启动一个实时打包的http服务器
- webpack-dev-server 自动打包生成的输出文件 ,是放在项目根目录的,而且是虚拟的看不见的,只能通过http://localhost:8080/bundle.js方式打开
- 配置自动打包参数
- open 自动打开浏览器
- host 配置 IP 地址
- port配置端口号
"dev" : "webpack-dev-server --open --host 127.0.0.1 --port 8888"
配置html-webpack-plugin 生成预览页面
-
安装插件: npm i html-webpack-plugin -D
-
修改webpacke.config.js 头部文件
//引入模块 const HtmlWebpackPlugin = require('html-webpack-plugin') const htmlPlugin = new HtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html' }) module.exports = { plugins:[htmlPlugin] }
webpack中的加载器(loader)
- webpack 只能打包 .js的文件
- loader 协助webpack打包
- 判断是否是.js文件
- yse 判断是否是高级js语法(es6es7)
- yes 判断是否配置了 babel
- yes 调用 loader处理
- no 报错
- no webpack处理
- yes 判断是否配置了 babel
- no 判断是否配置了对应的loader
- yes 调用 loader处理
- no 报错
- yse 判断是否是高级js语法(es6es7)
-
打包处理css文件
- npm i style-loader css-loader -D 安装处理css文件的loader
- 在webpack.config.js 的module 的rules数组中 添加 loader规则
//存放loader规则 module:{ rules:[ {test:/\.css$/,use:['style-loader','css-loader']} ] }
- test 正则 以什么结尾
- use对赢要调用的loader
- use中的loader顺序是固定的
- 调用顺序是从后往前的
-
打包处理less文件
- npm i less-loader less -D 安装处理css文件的loader
- 在webpack.config.js 的module 的rules数组中 添加 loader规则
{test:/\.less$/,use:['style-loader','css-loader','less-loader']}
-
打包处理scss文件
- npm i sass-loader node-sass -D 安装处理sass文件的loader
- 在webpack.config.js 的module 的rules数组中 添加 loader规则
{test:/\.scss$/,use:['style-loader','css-loader','sass-loader']}
-
配置postCSS 自动添加 css 兼容性前缀
- npm i postcss-loader autoprefixer -D
// 创建postcss.config.js 中 const autoprefixer = require('autoprefixer') module.exports = { //挂载插件 plugins:[autoprefixer] } // webpack.config.js 中 module 配置loader {test:/\.css$/,use:['style-loader','css-loader','postcss-loader']}
-
打包样式表中的img图片和字体文件
- npm i url-loader file-loader -D
- 在webpack.config.js 的module 的rules数组中 添加 loader规则
{test:/\.jpg|png|gif|bmp|ttf|eof|svg|woff|woff2$/,use:'url-loader?limit=19640'}
- ?后面是loader的参数项
- limit用来指定图片的大小,单位是字节(byte),小于limit大小的图片,会被转为base64图片(读取会更快)
-
打包处理js中的高级语法(es6 es7)
- npm i babel-loader @babel/core @babel/runtime -D 安装babel转换器相关的包
- npm i @babel/preset-env @babel/piugin-transform-runtime @babel/plugin-proposal-class-properties -D 安装babel语法插件相关包
- 在根目录 创建babel 配置文件 babel.config.js 并初始化
module.exports = { presets : ['@babel/preset-env'], plugins : ['@babel/plugin-transform-runtime','@babel/plugin-proposal-class- properties'] }
- 在webpack.config.js 的module 的rules数组中 添加 loader规则
{test:/\.js$/,use:['babel-loader'],exclude:/node_modules/}
- exclude : 排除项 里面填正则表达式 不会打包这个文件夹
-
vue 组件加载器
- npm i vue-loader vue-template-compiler -D
- 在webpack.config.js 的module 的rules数组中 添加 loader规则
const VueLoaderPlugin = require('vue-loader/lib/plugin') {test:/\.vue$/,loader:'vue-loader'} plugins:[new VueLoaderPlugin()]
单文件组件
-
后缀名为vue的文件
-
可以解决
-
需要配置对应的loader文件
-
template 定义模板内容
-
script 业务逻辑
-
style 样式区域
- scoped 防止各组件样式冲突问题
<template>
<div>
<h1>
这是App根组件
</h1>
</div>
</template>
<script>
export default {
data() {
return
}
}
</script>
<style scoped>
</style>
在webpack中使用vue
- npm i vue 安装vue
- src > index.js 入口文件中 ,通过 import Vue from ‘vue’ 来导入vue构造函数
- 创建vue实例,并制定要控制的el区域
- 通过render 函数渲染app根组件
import Vue from 'vue';
import App from './components/App.vue'
const vm = new Vue({
el:'#app',
render : h => h(App)
})
webpack打包发布
- 在package.json文件中配置webpack打包命令
- 该命令默认加载项目根目录中的 webpack.config.js 配置文件
"script" : {
//用于打包的命令
"build" : "webpack -p",
//用于开发调试的命令
"dev" : "webpack-dev-server --open --host 127.0.0.1 --port 8888"
}
- 使用 npm run build打包 (可以先将dist文件删除)
Vue 脚手架
- 快速生成vue基本架构
- 安装3.x版本的vue脚手架 npm install -g @vue/cli
- vue -V 查看vue脚手架 版本号
- 3.x支持高版本也支持低版本基本架构
创建新版vue项目
-
基于 交互式命令行 创建 vue create 项目名
-
基于 图形化界面 的方式 创建 vue ui
-
基于 2.x的旧模板 创建 旧版vue项目(了解)
- npm install -g @vue/cli-init
- vue init wepack my-project
脚手架项目自定义配置
- 在项目根目录创建 vue.config.json
module.exports = z{
devServer = {
"port":8888,
"open":true
}
}
Element-UI(命令行方式安装)
- 一个基于vue2.0的组件库
- 官网: http://element-cn.eleme.io/#/zh-CN
- npm i element-ui
- 导入Element-UI相关资源
//导入组件库
import ElementUi from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Element-UI(图形化界面安装)
- vue ui命令打开 图形化界面
- 通过Vue项目管理器 ,进入具体的项目配置面板
- 点击插件 > 添加插件 ,进入插件查询面板
- 搜索 vue-cli-plugin-element 并安装
Vue项目初始化步骤
- 安装Vue脚手架 npm install -g @vue/cli (全局安装 安装一次就不用再安装了)
- 通过Vue脚手架 创建项目 Vue ui
- 使用码云将项目上传到远程仓库
- 安装备份MySQL数据库 并导入数据库启用
- 安装服务器相关依赖包, 启动服务器 并使用PostMan测试路由接口
ESLint语法报错问题
- 项目根目录创建 .prettierrc 文件
{
"semi":false, // semi 格式化 末尾不会额外加分号
"singleQuote":true // 格式化 启用单引号
}
- 空格报错 打开.eslintrc.js 文件 rules对象中添加一条属性 (需要重新编译项目)
rules:{
'space-before-function-paren' : 0
}
Vuex
- 集中管理共享数据
- 解决了复杂的组件之间关系的数据传递
- 存储的数据是响应式的
- 易维护
Vuex的使用
- npm install vuex --save
// main.js 配置
import store from './store/index'
new Vue({
store,
router,
render: h => h(App)
}).$mount('#app')
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
//state中存放的就是全局共享数据
state: {
count: 0
},
mutations: {
},
actions: {
},
modules: {
}
})
state数据引用方式
//第一种
{{$store.state.count}}
//第二种引用方式(可以避免组件内直接对共享数据进行操作)
{{count}}
//script
import { mapState } from 'vuex'
computed: {
...mapState(['count'])
}
Mutation
- 修改state中的数据
- 不要执行异步操作
//store/index.js中
mutations: {
add (state) { //不带参数
state.count++
},
minus (state, step) { //带参数 形参只能有两个
state.count -= step
}
}
//组件中使用 this.$store.commit('函数名', 2) 2为传递的参数
methods: {
minus () {
this.$store.commit('minus', 2)
}
}
//第二种方式 先引入
import { mapMutations } from 'vuex'
methods: {
...mapMutations(['minus']),
//点击按钮触发事件
minus () {
// mapMutations中定义的
this.minus(2) //传递的参数写在括号内
}
}
action
- 用于进行异步操作(规范)
- Mutation中使用异步操作 vue tools插件监听不到数据的变化
- 回调中使用commit触发 Mutation 修改数据
// 定义
actions: {
addAsync (context) {
setTimeout(() => {
context.commit('add') //调用Mutation中的 add函数
}, 1000)
}
}
//组件 函数中
addAsync () {
this.$store.dispatch('addAsync')
}
//携带参数action
actions: {
addAsync (context, step) {
setTimeout(() => {
context.commit('add', step.num) //调用Mutation中的 add函数
}, 1000)
}
}
//携带参数 组件 函数中
addAsync () {
//为了避免action传多个参数会undefined的情况,把所有变量装到一个对象里传过去
this.$store.dispatch('addAsync', { num: 5 })
}
//action第二种调用方式
minusAsync (context, step) {
setTimeout(() => {
context.commit('minus', step.num)
}, 1000)
}
//引入
import { mapActions } from 'vuex'
//函数中映射为自己的函数
methods: {
...mapActions(['minusAsync']),
}
//调用
<button @click="minusAsync({ num: 3 })"> -N</button>
getters
- 对已有state数据进行包装 不会修改其数据 类似Vue中的计算属性
// store中定义
getters: {
showNum: state => {
return '当前count的最新值是【' + state.count + '】'
}
}
//组件中使用
{{this.$store.getters.showNum}}
// 使用方式二
import { mapGetters } from 'vuex'
computed: {
...mapGetters(['showNum']
}
Vue登录退出功能
- 处理好分支状态 并创建 登录子分支
- 梳理项目结构
- vue ui
- 任务> 运行 serve > 启动app(查看项目效果)
- 将不需要的结构删除
- 创建login.vue单组件
- 在主页中设置路由占位符
- 在路由js中配置login的路由 (并设置重定向访问/的时候直接访问login单组件页面)
- 搭建静态页面html 和css
- 由于使用的是less语法 需要 在element ui中 下载 less-loader 和 less 依赖
- https://element.eleme.cn 进入element官网复制需要的组件进行使用
- 由于安装的element是按需导入
- 所以每个组件要在element.js中导入进来
- 同时通过 Vue.use(组件名) 开放到整个项目
- 数据绑定
- data中定义return 对象属性:数据对象 loginForm
- 在表单上绑定自定义属性 :model=’ loginForm’
- 表单控件上添加 v-model=’ loginForm.属性名’
- 路由规则
- data中定义return 对象属性:路由规则对象 loginFormRules
- 在表单上绑定自定义属性 :rules=’ loginForm’
- 在对应的表单控件的盒子form-item 添加 prop属性 值为 loginFormRules.对应的路由规则
- 重置功能
- 表单添加一个 ref属性 值为实例对象名称
- 表单内的控件 的this 会添加一个 $refs属性
- 通过this.$refs.自定义ref属性值 就可以拿到表单的实例对象了
- 再通过element的表单控件方法 resetFields 就可以重置整个表单了
- 预验证
- 通过ref属性添加表单调用对象
- 通过表单调用对象方法 validate 可以获取验证结果(布尔值)
- 配置axios发送登录请求
- 在main.js中引入axios 并全局配置axios默认地址
- 在Vue原型对象上添加 axios 这样所有组件中都可以通过this 来访问axios
- 查看接口文档 在表单预验证通过后 发送ajax请求
- 接收返回值并做响应的处理
- 全局弹窗组件Message
- element中引入Message
- 在Vue的原型对象上挂载Message
- 组件中通过 this 可以访问挂载上去的message方法
- 登录保持 token
- 将服务器返回的数据中的token属性 放入sessionStorage中
- window.sessionStorage.setItem(‘token’, res.data.token)
- 并通过this.$router 重定向到home页面
- 创建home.vue并搭建基本结构 在router.js中引入并配置路由
- 路由导航守卫/登录拦截
- 在router.js中 使用路由的对象的方法 beforeEach((to, from, next) =>{})来设置登录拦截
- to将要访问的页面路径 from 从哪个页面路径跳转而来 next放行函数
- 当window.sessionStorage中没有token属性时 next(’/login’) 强制跳转到登录页面
- 当访问登录页面和 sessionStorage中由token属性时 则next() 放行
- 退出登录
- 添加按钮触发函数
- window.sessionStorage.clear() 删除token
- this.$router.push(’/login’) 重定向登录页面
- 提交代码到码云远程仓库
- git add . 提交代码到缓存区
- git commit -m “完成了登录功能” 提交代码
- git checkout master 切换到主分支
- git merge login 合并(更新) 登录分支
- git push 上传代码到远程仓库
- git checkout login 切换到登录分支
- git push -u origin login 将登录分支上传到远程仓库 并命名为login分支
Vue用户列表功能实现
- element 寻找适用 container容器组件 搭建home.vue文件基本结构
- element.js中引入对应的element组件
- Vue.use 开放到所有组件中
- 通过axios请求拦截器添加token
- main.js中 在将axios添加到请求头之前 创建一个请求拦截器
- 请求拦截器中 为请求头添加一个后台要求的属性 并赋值token
- 获取左侧菜单数据
- created vue实例对象创建完毕时调用一个函数
- 函数中 通过axios请求获取左侧菜单数据
- data中创建一个数组 存放获取的左侧菜单数据
- 渲染左侧菜单
- 利用双重v-for 渲染左侧菜单
- 外层和内层的 当前循环元素 item 不能一样
- index 需要设置不同的值 不然会同时展开 可以设置为传递过来的id
- 分类图标
- data中创建一个图标对象
- 图标的键设为 对应数据的id 值 设置对应的图标字符串
- :class=“图标对象[循环体.id]”
- 每次只打开一个菜单
- el-menu标签添加 unique-opened 属性
- 侧边栏折叠与展开
- 创建一个按钮用于折叠与展开
- 创建一个值为布尔值的data属性 (展开与折叠开关)
- 点击按钮时触发函数 取反开关属性布尔值
- 属性绑定用于折叠与展开的属性 并将开关属性作为值
- 首页路由重定向
- 创建welcome.vue 并搭建基本结构 router.js中引入
- 在el-main中位置设置一个路由占位符
- 路由占位符在/home中 所以 /home路由要嵌套新路由作为子路由 并重定向到这个子路由
- 侧边栏路由链接改造
- el-menu 添加router 属性 表示开始路由模式
- :index 绑定的属性应改为服务器传递过来的path路径 由于没有/ 所以前面要加一个/
- 路由展示用户列表页面
- 创建 users.vue 并搭建基本结构 router.js中引入
- 由于替换掉的是welcome.vue页面
- 所以在welcome同级创建路由即可
- 侧边栏激活状态
- 侧边栏展开是依靠index值的
- 点击时将对应的index值存入sessionStorage 中
- 当页面实例创建完毕时将sessionStorage中的值 赋值给 一个容器
- 菜单激活状态属性 default-active 的值 使用容器的值
- 绘制用户列表组件基础布局结构
- 利用 面包屑 卡片 input 和 栅格系统layout分栏间隔布局
- 获取用户列表参数
- 在created创建好实例对象时 调用函数发送ajax请求获取用户列表参数
- 并将用户列表数组和当前页码存入data数据
- 使用el-table组件渲染基本用户列表
- el-table 的:data 属性中 放数据中的用户列表数组
- el-table-column 的prop属性中 放数组中循环对象的属性名 label属性中输入表头名称
- 索引列 用el-table-column 只设置type属性 值为index
- 自定义状态列的显示效果
- 状态栏中创建一个template 并设置属性slot-scope 获取作用域插槽中的所有数据
- 通过slot-scope设置的名字 .row 获取当前行所有数据
- 再用el-switch双向数据绑定v-model 设置布尔值来实现开关状态
- 通过作用域插槽渲染操作列
- 实现分页效果
- 利用pagination组件实现分页效果
- 修改用户状态
- 为开关添加@change事件 并将 slot-scope当前行的所有数据传递到函数中
- 函数中发送请求修改用户状态数据
- 实现搜索功能
- 为搜索按钮添加点击事件调用之前设置 的获取用户列表函数即可
- 渲染用户的对话框
- 使用dialog 组件
- 渲染添加用户的表单
- 在dialog 组件 中 嵌套 form组件即可
- form属性绑定 :rules 可创建 路由规则对象设置规则
- form-item 添加 prop 可以匹配对象规则中的 属性名的规则
- 自定义校验规则
- data中创建校验规则 (rule,value,cb)=> {} 在其中定义正则的处理代码
- value为当前表单内容 cb为回调函数
- 在对应规则属性中 {vailddator: 创建的校验规则 , trigger : ‘blur’} blur 失去焦点触发
- 表单关闭后重置表单
- 通过表单引用对象 的方法resetFields()
- 添加用户
- 通过表单引用对象方法 validata判断表单预验证通过没
- 通过了 发送ajax请求 接收返回值
- 未成功 return 提示信息
- 成功则关闭对话框并调用获取用户列表数据
- 修改用户
- 创建点击事件 创建修改用户对话框
- 点击事件中 发送请求获取用户数据 显示在对话框中 并数据校验
- 创建提交点击事件 效验成功时 发送请求并将表单内的数据作为请求参数
- 修改不成功则return 并提示 修改成功则删除对话框 并更新数据
- 删除用户
- 添加点击按钮删除事件并将id传递过去
- 根据ID 发送请求 接收返回值
- 失败 return 错误提示 成功 刷新用户数据列表
- 创建user子分支并提交到远程仓库
- git checkout -b user 切换到-b 新建的user分支上
- git add. 提交代码到暂存区
- git commit -m “完成用户列表功能” 提交代码到本地仓库
- git push -u origin user 提交代码到远程仓库 新建的user分支
- 由于代码都切换带到user分支了 所以 master 还是上次提交login分支的进度
- git checkout master 切换到主分支
- git merge user 合并user分支代码
- git push 上传到远程仓库更新 master主分支代码
Vue权限管理功能
- 创建rights权限分支
- git checkout -b rights 切换并创建rights分支
- git push -u origin rights 在远程仓库新建rights分支 并将当前进度提交上去
- 创建权限列表页面并渲染
- 创建rigets.vue 并添加路由
- 使用 面包屑 卡片 表格组件搭建页面基本结构
- 创建获取权限列表信息函数 并赋值给存放数据的模板数据
- 页面搭建完毕时调用获取权限列表函数
- 表格中根据 模板数据渲染表格信息
- 创建角色列表页面并渲染
- 创建roles.vue 并添加路由
- 和权限列表基本一致
- 表格第一列为展开列 里面为权限分配展示
- 使用三重v-for数据渲染 使用分栏间隔布局
- 添加角色
- 创建添加角色对话框 并在里面放入一个Form表单并双向绑定一个数据模板 表单通过绑定rules和porp属性进行表单验证
- 点击按钮触发函数弹出对话框
- 点击确认触发函数 先使用表单引用对象的validate属性判断是否通过预验证 再发送ajax请求添加角色
- 添加成功后 关闭对话框 并调用函数刷新角色列表
- 为form表单绑定colse事件 对话框关闭时触发函数 清空在对话框中使用过的双向绑定的数据模板
- 删除角色
- 点击按钮触发函数并通过作用域函数slot-scope 将当前行的数据传递到删除角色函数中
- 函数中弹出confirm组件 当用户点击确认后 根据传递过来的ID发送请求删除角色 并调用函数刷新角色列表
- 编辑角色
- 创建编辑角色对话框 在里面放入一个Form表单并双向绑定一个数据模板 表单通过绑定rules和porp属性进行表单验证 设置一个数据模板存储当前角色id
- 点击按钮触发函数弹出对话框
- 点击确认触发函数 先使用表单引用对象的validate属性判断是否通过预验证 再根据数据模板中的角色id发送ajax请求编辑角色
- 编辑成功后 关闭对话框 并调用函数刷新角色列表
- 为form表单绑定colse事件 对话框关闭时触发函数 清空在对话框中使用过的双向绑定的数据模板
- 删除角色下制定权限
- 为标签添加closable属性可提供一个x按钮
- 创建close事件 关闭tag标签时触发删除权限函数 并将当前行的数据和当前权限ID传递过去
- 函数中使用弹框组件 当点击确认时 调用接口删除对应id权限
- 由于重新调用接口获取数据会关闭扩展列 所以 将服务器传递过来的新数据赋值给 当前行数据(传递给函数的当前行数据)
- 弹出分配权限对话框
- 创建分配权限对话框 点击时将对话框开关改为true打开
- 同时调用函数 获取所有权限列表信息 (使用tree类型) 保存在数据模板中
- 在对话框中使用tree组件 根据数据模板渲染 tree组件
- 分配权限默认勾选
- tree组件中 添加树组件勾选框和 默认展开全部属性
- 再添加勾选框绑定id属性 和根据数组默认勾选 勾选框属性
- 点击展开分配权限对话框函数中 调用递归函数 将本行数据 和 一个用于存储 勾选id的模板数据传递进去
- 递归函数中 判断是否有childeren属性 来确认是否是第三级权限对象 当是第三级时 将当前形参的id push添加到 模板数据中的数组中 并return 返回
- 当不是第三级权限时 循环当前数据形参的children数组 调用该递归函数并将当前循环项和 存储id模板数组传递进去
- 调用API分配角色权限
- 使用 tree 两个方法获取 全选 和半选 勾选框所对应权限ID
- 根据 ID 发送 ajax 请求 接收返回值 逻辑处理 后 重新获取角色列表信息
- User页面用户分配角色
- 创建一个带有下拉框的分配角色对话框
- 点击分配按钮将当前行数据传递到 显示分配角色对话框函数中,并将当前行的数据渲染到对话框,同时发送请求获取所有角色数据并渲染到下拉框中
- 下拉框中将 角色的id设为 模板数据 点击提交时 使用当前用户id和选择角色id发送请求分配角色
- 服务器返回数据逻辑判断成功时 重新获取用户列表数据并将对话框关闭
- 提交角色列表功能分支
- git add . 将当前rights分支代码提交到缓存区
- git commit -m “提交信息” 将缓存区代码提交到本地仓库
- git push 将本地仓库上传到远程仓库中
- git checkout master 切换到主分支
- git merge rights 合并新完成的子分支
- git push 再将主分支的代码更新到远程仓库中
Vue商品分类功能
- 新建 商品分类分支
- git checkout -b goods 切换并新建商品分类分支
- git push -u origin goods 将新分支同步到远程仓库中
- 创建goods.vue 页面
- 搭建页面基本结构 并配置路由
- 添加 面包屑 卡片组件 和 添加分类按钮
- 商品分类列表数据渲染
- vue实例化函数中 调用获取商品列表数据函数 将获取的商品列表数据保存在数据模板中
- 使用table 组件的树形数据模式 在:data属性值中放入商品列表数据数组 row-key属性值 中放入每个数据的id属性名 :tree-props中的children属性值中放入子数组名
- table-column 列的属性中 label 为表头 prop 放入当前数据的属性名
- 是否有 和 排序 和操作 使用 作用域插槽 放入 自定义组件 并根据 slot-scope 获取当前行数据 渲染组件
- 添加商品分类
- 创建添加分类对话框 点击添加分类时弹出对话框
- 创建form表单 并创建 添加分类 输入框 并双向数据绑定 一个用于发送ajax请求的数据模板
- 调用函数 获取所有父级分类数据 并保存在数据模板中
- 使用Cascader 级联选择器组件 渲染所有父级分类数据
- options 值为所有父级数据模板
- props 在数据模板中定义一个对象
- value 属性值为选中的属性
- label 显示的值为选中的属性
- children 子级数组根据哪个属性渲染
- v-model 双向数据绑定 选中的数据 在数据模板中创建这个数组
- 使用 @change事件 当选中某个选项时触发 当数据模板中用于存放选中数据的数组长度大于0时 说明有数据 将 数组中的最后一项 和 当前数据所在层级(数组长度) 保存在用于添加分类的数据模板中
- 点击确认时通过表单引用对象预验证表单
- 通过后 发送请求 添加分类 返回status 为正确时 调用函数刷新商品分类列表 并 关闭对话框
- 提交商品分类模块
- git add . 提交代码到缓存区
- git commit -m “提交信息” 保存到本地仓库
- git push 上传到远程仓库
- git checkout master 切换主分支
- git merge goods 合并新完成的代码
- git push 上传到远程仓库的主分支
Vue 商品管理 分类参数 功能
- 创建 goods_params分支
- git checkout -b goods_params 新建分支
- git push -u origin goods_params 同步到远程仓库
- 搭建goods_params基本结构
- 创建goods_params.vue 页面 并在router 路由中设置路由规则
- 搭建goods_params 基本骨架 并创建面包屑导航和 card卡片区域
- 添加一个警告组件 用于提示用户操作
- 渲染商品分类的级联选择框
- 页面实例创建完毕函数中 调用函数获取所有商品分类数组并保存在数据模板中
- 创建cascader组件并进行配置
- options 渲染的数据数组
- props 渲染的数据配置对象
- value 选中值的id
- label 显示对应的数据名
- children 渲染子级数组名
- expandTrigger 展开子级的方式
- @change 选中状态发生改变时触发的函数
- 渲染动态参数和静态属性区域
- 引入Tabs标签页组件 根据name 显示不同页面
- 每个tab-pan标签中添加一个用于添加数据的按钮 和一个渲染数据的表格
- 在级联选择器选中时@change事件和 标签页切换时@tab-clic事件的触发函数中 调用获取参数的函数
- 根据级联选择器选择的数据长度判断是否选中了第三级数据 再 根据标签页的name 属性值 来判断当前在时在动态参数页还是静态属性页
- 获取以上两点成立的数据 发送请求 获取数据 并保存在不同的数据模板中
- 表格中根据获取的数据来渲染 动态参数页和 静态属性页
- 添加动态参数 和静态属性
- 创建添加对话框 点击按钮时触发 (两个按钮共用一个函数)
- 根据 标签页的v-model选中属性值 来渲染对话框
- 对话框中创建表单 并设定验证规则 对话框关闭函数中 利用引用对象情况表单的内容
- 点击确认触发函数 通过引用对象预验证表单 通过后 根据 数据模板中的 输入框的值 和 标签页所在页name的值 发送请求添加数据 路径中的当前分类id 可以在计算属性中获得
- 添加成功后 调用函数重新获取数据列表 并关闭对话框
- 修改动态 或静态属性
- 创建添加对话框 根据 标签页的v-model选中属性值 来渲染对话框
- 点击按钮时触发函数函数显示对话框 (两个按钮共用一个函数) 并将当前行的数据传递进去
- 函数中根据传递过来的当前行数据保存当前参数id 为输入框的双向绑定数据 赋值参数名
- 对话框中创建表单 并设定验证规则 对话框关闭函数中 利用引用对象情况表单的内容
- 点击确认触发函数 根据数据模板中的数据和计算属性的第三级数据id发送请求 修改数据
- 修改完成后 关闭对话框并刷新数据列表
- 删除动态 或静态属性
- 点击触发函数 并将当前行参数id传入函数中
- 函数中使用$comfirm显示确认弹框
- 点击确认时 根据计算属性的第三级数据id和参数id发送请求删除数据
- 渲染参数下的可选项
- 为表格添加一个扩展列 扩展列中使用 作用域插槽嵌套标签组件
- 当级联选择器选中和 标签页变动 触发的函数内 根据作用域插槽传递过来的id 获取 可选项 数据
- 利用v-for 渲染tag组件内容 并且 数据模板挂载在当前作用域下 可以使每个作用域都是独立的
- input输入框 可以使用this.$nextTick() 回调 让输入框自动获取焦点
- 使用动态编辑标签 当触发回车和失去焦点事件时 将 输入框的内容 添加到当前作用域的标签数据数组中 并根据数组发送请求添加标签接选项 添加完成后 清空输入框内容 并 关闭输入框
- 删除可选项
- 标签组件关闭事件中 将索引 和当前行数据传递给关闭事件函数
- 函数中 使用$comfirm 确认用户是否确认删除
- 确实后根据传递过来的数据 发送请求 删除可选项
- 提交商品分类参数模块
- git add . 提交代码到缓存区
- git commit -m “提交信息” 保存到本地仓库
- git push 上传到远程仓库
- git checkout master 切换主分支
- git merge goods_params 合并新完成的代码
- git push 上传到远程仓库的主分支
Vue商品列表功能
-
创建list权限分支
- git checkout -b goods_list 切换并创建rights分支
- git push -u origin goods_list 在远程仓库新建rights分支 并将当前进度提交上去
-
搭建页面基本结构
- 创建list.vue页面 并配置路由
- 搭建 面包屑 卡片 搜索表单组件
-
渲染商品数据列表
- 创建表格组件
- 创建获取列表数据函数 并配置数据模板 在created 页面实例化函数中 调用
- 将获取的列表数据保存在数据模板中 并使用表格组件 渲染表格
-
自定义全局过滤器
- main.js中配置
Vue.filter('dateFormat', function(originVal) { const dt = new Date(originVal) const y = dt.getFullYear() const m = (dt.getMonth() + 1 + '').padStart(2, '0') const d = (dt.getDay() + '').padStart(2, '0') const hh = (dt.getHours() + '').padStart(2, '0') const mm = (dt.getMinutes() + '').padStart(2, '0') const ss = (dt.getSeconds() + '').padStart(2, '0') return `${y}-${m}-${d} ${hh}:${mm}:${ss}` })
- 表格中通过作用域插槽 {{当前行渲染数据 | dateFormat}} 使用过滤器
-
实现分页效果
- 利用pagination组件实现分页效果
-
搜索和删除商品
- 为搜索输入框双向数据绑定 商品列表的数据模板
- 当点击输入框时调用获取商品列表函数即可
- 为删除按钮添加作用域插槽和点击事件
- 点击事件触发函数并将当前行的商品id传递给函数
- 函数中通过商品id发送请求删除商品 并重新获取商品列表
-
添加商品
- 点击添加商品按钮 创建添加商品页面和路由 跳转至添加商品页面
- 渲染页面基本机构 并引用步骤组件 和标签页组件
- 将步骤组件的active属性 和标签页显示的v-model 的值设为同样的索引 即可达到联动效果
- 创建级联选择器 页面加载时请求获取分类数据 渲染级联选择器
- 使用标签页before-leave回调 当前索引变化时 选择的分类数据数组长度不等于3就返回false阻止标签页切换
- 使用标签页点击事件 当点击商品参数标签页时 发送ajax请求 获取商品动态参数
- 商品参数标签页引用多选组组件 由于vals属性是字符串 获取动态参数的返回值数组要使用循环并将vals属性通过split(’ ')方法加工为一个个数组 并赋值给返回值中
- 使用标签页点击事件 当点击商品属性标签页时 发送ajax请求 获取商品静态属性 在表单中 通过for循环商品静态属性 渲染 label属性值和输入框双向绑定数据值
-
图片上传和预览
- 引入上传组件 属性绑定headers 配置请求头
- on-success 上传成功回调中 将当前图片的路径push到添加商品数据模板中
- on-remove 根据当前图片的临时路径 查询在数据模板中的索引并删除
- 创建一个对话框 对话框里包含一个图片 为图片绑定一个路径数据模板
- on-preview 点击预览图片时 获取当前图片的项目路径 保存 在数据模板 开启对话框
-
富文本编辑器依赖
- npm install vue-quill-editor
- 在main.js中 引入 vue-quill-editor及依赖 并开放到全局组件
- 组件中通过标签使用 并未其双向数据绑定到添加商品数据模板中
-
添加商品
- 点击按钮触发添加商品函数
- 函数中使用表单引用对象预验证表单
- 有的请求数据需要转换为字符串 但是由于是双向数据绑定 所以需要深拷贝一份添加商品数据模板 这里可以使用 lodash 依赖
- npm i lodash
- import _ from ‘lodash’ 引入
- _.cloneDeep(克隆对象)
- 循环动态参数和静态参数 数据模板 的 id和 val值 按照接口文档 封装为一个对象 并添加到添加商品数据模板的attrs数组中
- 根据克隆的数据模板发送请求添加商品 成功添加则跳转到商品列表页面
- 提交代码到码云中
Vue订单列表功能
- 创建order订单分支并搭建页面基本结构
- 使用git命令创建并切换到order分支 并同步到码云
- 创建order.vue添加路由 并搭建基本结构 面包屑和卡片组件
- 渲染订单列表
- 页面搭建完毕时发送请求获取订单列表数据
- 创建表格组件 并根据订单列表数据渲染表格
- 渲染分页
- 根据订单数据列表中的数据创建并渲染分页组件
- 修改地址
- 为按钮添加点击事件 弹出对话框 并放入表单组件
- 使用import 引入 省市区三级联动js文件 并在data 中保存其数据
- 使用级联选择器 双向数据绑定引入的数据
- 物流进度
- 创建物流进度对话框
- 点击按钮展示对话框 并发送请求获取物流进度数据
- 在element中 通过import 引入时间线组件 的js文件 并开放到全局中
- 对话框中创建时间线组件 并通过请求获取的数据渲染组件
- style 中 使用@import 引入时间线组件的css文件
- 将代码同步到码云
数据统计
- 创建report订单分支并搭建页面基本结构
- 使用git命令创建并切换到report分支 并同步到码云
- 创建report.vue添加路由 并搭建基本结构 面包屑和卡片组件
- 渲染数据列表
- npm i echarts 下载渲染图标的依赖
- import 引入图标依赖 和lodash合并对象依赖
- mounted 回调中 发送请求获取数据获取数据列表数据
- 将返回的数据 和接口中需要合并的数据对象 使用 lodash依赖的merge方法 合并为一个对象
- 按照图表教程将合并的数据显示即可
优化
- nprogress优化进度条效果
- npm install nprogress
- 通过import 引入nprogress 及其样式
- 在axios 请求拦截器中 调用 引入对象的start()方法
- 在axios 响应拦截器中 调用 引入对象的 done()方法
- 移除所有console.log
- npm install babel-plugin-transform-remove-console -D 在开发依赖中安装
- 根据process.env.NODE_ENV === ‘production’ 判断为发布模式时
- babel.config.js中plugins数组中添加 ‘transform-remove-console’ 就可以移除所有的console
- 需要重新运行项目才可生效
$
- this.$nextTick() 当页面渲染之后调用其中的回调 可用于获取表单焦点
小技巧
- 如果页面获取的数据是数据库全部的数据 就不用再发送请求查询数据了,直接对获取的数据进行查询即可
- vs-code插件 : prettier - code formatter 格式化插件