我没系统学过VUE和element-ui,都是要用了再官网上查找,或者百度
1、element中调用方法
再普通vue中调用方法是标签中写@click="方法名"
,然后再在方法中vue实例中
methods:{
方法名(){
alert("123");
//code...
}
}
但是在element-ui元素中,要 @click.native=方法名"
,调用的一样
实例
<template>
<el-pagination
background
layout="prev, pager, next"
:total="1000"
@click.native="showSome">
</el-pagination>
</template>
<script>
export default {
methods:{
showSome(){
alert("")
}
}
};
</script>
2、有些element自带事件和参数
还是上面那个例子,打开官网往下走,看到
这个current-page参数就是页数,那么事件往下面翻
看到,current-page改变时,事件名为current-change,并且有个回调参数为当前页,那么
<el-pagination
background
layout="prev, pager, next"
:total="1000"
@current-change="page">
</el-pagination>
<script>
export default {
methods:{
page(currPage){
alert(currPage)
}
}
};
</script>
- 调用方法,不是
@click
而是@current-change
,因为上面也说了事件是这个。。然后这个事件,定义个方法名吧,就叫page
,在下面写方法page,其有个回调参数,参数名就自己瞎写了,就可以用到了
3、分页功能
使用分页功能的时候,其有个属性为page-size
(每页显示条目个数),这个记得设置,不然默认是10 ,然后:total
是(总数目),这个也要记得设置,然后页数就是:total
/page-size
4、axios得get与post
5、跳转并且传值
就是点击一个按钮,路由改变,并且传递参数
其跳转传值有:<router-link>
直接传值跳转 和 方法内的this.$route.push(query:{})
接受值有2中方法,是params和query接受
所以是2*2=4种跳转传值方法
1)router-link + params
路由:
{
path:'/test',
name:'Test',
component:Test
},{
//占位符
path:'/test/:id',
name:'Test2',
component:Test2
}
<!-- test -->
<template>
<div>
我是test1
<!-- 1、直接通过 router-link 加上地址传值,id就是用到占位符了-->
<router-link :to="'/test/'+id">点我跳转</router-link>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
id:123
}
}
}
</script>
<!-- test2 -->
<template>
<div>
我是跳转的
<h2>{{id}}</h2>
<h2>{{this.$route.params.id}}</h2>
</div>
</template>
<script>
export default {
computed:{
id(){
//从路由中的占位符拿到数据
return this.$route.params.id
}
}
}
</script>
这是用computed方式的,用created来接受然后赋值也是可以的
2)router-link + query
这个就不要用到路由注册表了,用一般的就行
{
path:'/test',
name:'Test',
component:Test
},{
path:'/test2',
name:'Test2',
component:Test2
}
<!-- test -->
<template>
<div>
我是test1
<!-- 一样是 :to ,但是又path和query,query传对象的 -->
<router-link
:to="{
path:'/test2',
query:this.person}">
点我跳转</router-link>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
person:{
name:'lihua',
id:666
}
}
}
}
</script>
<!-- test2 -->
<template>
<div>
我是跳转的
<h2>name:{{person.name}}</h2>
<h2>id:{{person.id}}</h2>
<h2>{{this.$route.query}}</h2>
</div>
</template>
<script>
export default {
computed:{
person(){
//用this.$route.query来接受刚才传过来的值
return this.$route.query
}
}
}
</script>>
3)$router.push + params
这个还是要占位符的
{
path:'/test',
name:'Test',
component:Test
},{
//占位符
path:'/test/:id',
name:'Test2',
component:Test2
}
<!-- test -->
<template>
<div>
我是test1
<!-- 1、用点击事件去完成-->
<button @click="tourl">点我跳转</button>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
id:666
}
},
methods: {
tourl(){
//点击事件,就传送到xxx + id
this.$router.push('/test/'+this.id)
}
}
}
</script>
<!-- test2 -->
<template>
<div>
我是跳转的
<h2>{{id}}</h2>
<h2>{{this.$route.params.id}}</h2>
</div>
</template>
<script>
export default {
computed: {
id() {
//从路由中的占位符拿到数据
return this.$route.params.id
}
}
}
</script>
4)$router.push + query
{
path:'/test',
name:'Test',
component:Test
},{
path:'/test2',
name:'Test2',
component:Test2
}
<!-- test -->
<template>
<div>
我是test1
<button @click="tourl">点我跳转</button>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
person:{
name:'lihua',
id:666
},
}
},
methods: {
tourl(){
this.$router.push({
//一样又path和query,然后那边就query接受
path:'/test2',
query:this.person
})
}
},
}
</script>
<!-- test2 -->
<template>
<div>
我是跳转的
<h2>name:{{person.name}}</h2>
<h2>id:{{person.id}}</h2>
<h2>{{this.$route.query}}</h2>
</div>
</template>
<script>
export default {
computed: {
person() {
return this.$route.query
},
}
}
</script>
5)总结
总的来说,query是接受对象,params接受一个key的。
router-link用来做a标签的简单跳转,$router.push用在方法里面更灵活
params需要路由占位符,query不需要
5.5、跳转和传值(笔记太垃圾已废弃,具体看5)
1、this.$router.push(“xxx”)
在方法中使用this.$router.push("/xxx")
,就可以跳到指定路径,从而配合router-view
.
1. 不带参数
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
2. query传参
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
3. params传参
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
4. query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
2、在el-menu
标签中添加router
属性
这个方法不一定实用于所有,因为el-menu中有el-menu-item子标签,而这个子标签的index属性就是跳转的路径,这个是特殊的
- 在
el-menu
标签添加router
属性 - 添加
router-view
el-menu-item
为选项的标签,其index
值就是跳转的router
5.6 跳转传值(笔记太垃圾已废弃,具体看5)
1)
传
<router-link
:to="{
path:'/qa/'+ problem.id,
params:{
id:problem.id
}
}">{{problem.title}}</router-link>
- path是要传得值,我这里是
/qa/id
加上id号跳转 - params是传值,key-value
收:
mounted() {
this.problem.id = this.$route.params.id
console.log(this.problem.id)
}
2)router-view传值
在路由弄一个占位符,然后跳到这里时,根据url获得值
在router 的index.js中
{
path: '/spit',
name: 'SpitIndex',
component:SpitIndex
},{
path:'/spit/:id',
name:'SpitItem',
component:SpitItem
}
//spit.vue
<router-link :to="'/spit/'+spit._id">{{spit.content}}</router-link>
这里跳转就传如ID了
在跳到到另外一个页面的时候
///spit/:id
console.log(this.$route.params.id)
//打印出跳转的ID
6、<template slot-scope="scope">
这个东西,很神奇,一般这样用,只能用于template的属性
<el-table
:data="tableData"
border
style="width: 40%">
<el-table-column
fixed
prop="id"
label="ID"
width="150">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="120">
</el-table-column>
<el-table-column
prop="author"
label="作者"
width="120">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
<el-button type="text" size="small">编辑</el-button>
</template>
</el-table-column>
</el-table>
<script>
export default {
methods: {
test(){
this.$router.push("/addbook")
}
}
}
</script>
点一下查看,就是26.27行的那些代码,定义了一个slot-scope="scope"
,利用什么滚动的。。我也不懂。然后下一行@click="handleClick(scope.row)"
,表示点这个查看用到这个方法,并且有个参数,方法中,传的这个scopne.row
,可以展示这个框的数据
7、components
就是复用代码了
使用步骤
- 写复用代码vue
- 在要用的地方
import xxx from "xxxx";
<xxx></xxx>
8、导入css文件
方案1、在main.js中引入方式(全局)
import '@/assets/css/reset.css'
方案2、在.vue文件的
@import "../assets/css/index.css";
9、关掉
根目录的.eslintrc.js
module.exports = {
root: true,
env: {
node: true
},
'extends': [
// 'plugin:vue/essential',
'@vue/standard'
],
rules: {
// 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
// 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
},
parserOptions: {
// parser: 'babel-eslint'
}
}
10、float失败的时候
有时候用了float后,button就不起作用了,style加上这个
style="display: inline-block;"
11、父子组件传值
1)父传子
①通过props的字符串传值
父
<template>
//用了子组件,传一个key为id,value则为bookid的值,传给子组件读取
<UserData :id="bookid"></UserData>
</template>
<script>
import UserData from '../../components/UserData'
export default {
name: "FriendIndex",
components: {
//加载儿子
UserData
},
data(){
return{
bookid:123
}
}
<script>
子:
<template>
<div>
//这里读取id,而这个id就是在子组件的props中拿到的
{{id}}
</div>
</template>
<script>
export default {
name: "",
//这里的props为数组形式,不是一个数组形式,就是保存父给子的key,写在这里
props:["id"],
}
</script>
<style scoped>
</style>
②通过props的对象形式传值
对象的更好,在第一个方法的基础上,添加了限定类型和默认值,其中,类型可以有
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
父
<template>
//用了子组件,传一个key为id,value则为bookid的值,传给子组件读取
<UserData :bookName="bookName" :price="price" :stroe="stroe" :obj="obj" :doubleType="doubleType"></UserData>
</template>
<script>
import UserData from '../../components/UserData'
export default {
name: "FriendIndex",
components: {
//加载儿子
UserData
},
data(){
return{
bookName:'金瓶梅',
price:666,
stroe:['新华书店','旧华书店'],
obj:{
msg:'hello'
},
doubleType:'123'
}
}
<script>
子:
<template>
<div>
//这里读取id,而这个id就是在子组件的props中拿到的
书名:{{bookName}} 价格:{{price}}
</div>
</template>
<script>
export default {
name: "",
//props变为对象形式,更方便
props:{
//单单限制类型
bookName:String,
//限制类型并且提供默认值
price:{
type:Number,
default:0,
//表示这个key值必须传东西进来
required:true
},
//如果是数组或者对象类型,那么默认值是要写一个函数返回的类型
stroe:{
type:Array,
default: () => []
},
//对象类型
obj:{
type:Object,
default(){
msg:'hello'
}
},
//即可是数组,也可是数字
doubleType:[String,Number]
}
}
</script>
<style scoped>
</style>
③大坑,子接受不到数据
就直接{{}}
可以显示数据,但是在方法或者created中想用就用不了,
就很诡异,没有值,原因是这父传子需要异步处理,所以有时候会延迟,导致传不了数据到子组件的created和mounted,所以解决办法是在子组件加上个v-if
进行判断
如
//在后面加个判断,这样有才进行传值,就不会异步了
<LastPhysicalCardSale :date="lastDate" v-if="lastDate"/>
2)子传父
比如发生点触事件,传值给父组件给父组件显示
思路为,子组件写一个@click点击事件
,事件里面写this.$emit(事件名,要传的参数)
,这里就完成了发送事件。
接着就是在父组件里面写一个事件 用于接受参数 的函数,函数的参数就是接受到的数
父
<template>
<div>
//3、父组件用到子组件,父传子的话要加一个 @事件 的监听事件,事件名是子组件中$emit定义的,函数名就自己写
<TestSon @itemClick="sonClick"></TestSon>
{{ball}}
</div>
</template>
<script>
import TestSon from './testSon.vue'
export default {
name: "Test",
data() {
return {
ball:''
}
},
methods: {
//4、这里父接受子的事件,函数写一个参数就能接受到了
sonClick(item){
console.log('item',item)
this.ball=item
}
},
components:{
TestSon
}
}
</script>
子
<template>
<div>
//1、点击事件触发函数,传值给父组件,用@click,
<button v-for="item in list" @click="btnClick(item)">{{item}}</button>
</div>
</template>
<script>
export default {
name: "TestSon",
data() {
return {
list:['篮球','足球','羽毛球']
}
},
methods: {
//2、写函数,里面一定要这样写 this.$emit('父组件接受的事件',值)
//注意是父组件接受的事件而不是函数
btnClick(item){
this.$emit('itemClick',item)
}
},
}
</script>
点篮球
点足球
11.5、父子组件的访问
访问是只能拿到值,读取,并且可以调用方法
1)父访问子
①$children方法访问
父
<template>
<div>
<TestSon ></TestSon>
<button @click="btnClick">按钮</button>
</div>
</template>
<script>
import TestSon from './testSon.vue'
export default {
name: "Test",
data() {
return {
}
},
methods: {
//2、父组件访问子组件通过$children访问,得出结果是个数组,可以拿到子组件的很多东西
btnClick(){
console.log('父组件打印子组件',this.$children)
console.log('父组件打印子组件的变量:'+this.$children[0].message);
this.$children[0].showMessage()
}
},
components:{
TestSon
}
}
</script>
<style scoped>
</style>
子
<template>
<div>
</div>
</template>
<script>
//1、子组件这里只提供一个变量和方法,示范是父组件访问子组件的变量和方法
export default {
name: "TestSon",
data() {
return {
message:'这是一句话'
}
},
methods: {
//这是一个方法
showMessage(){
console.log('子组件里面打印:'+this.message)
}
},
created() {
},
props:{
}
}
</script>
<style scoped>
</style>
打印子组件可以有很多东西的
$children的缺陷
比如父组件写很多个子组件
<TestSon ></TestSon>
<TestSon ></TestSon>
<TestSon ></TestSon>
$children
是一个数组,取第二个的值是
this.$children[1]
这样取的,但是如果某一天在第一个后面插入一个子组件,那么数组下标就变了,所以很不人性化,这时候就要使用$refs
了
②$refs方法访问(推荐)
因为$children
的缺陷,$children
是数组形式,$refs
是使用key的形式,每一个子组件加一个ref
标识,在读取的时候根据ref
名字读取即可
子和上面的一样
父
<template>
<div>
<TestSon ></TestSon>
//1、给每一个子组件加一个ref的名字
<TestSon ref="son1"></TestSon>
<TestSon ref="son2"></TestSon>
<button @click="allSon">按钮1</button>
<button @click="oneSon">按钮2</button>
</div>
</template>
<script>
import TestSon from './testSon.vue'
export default {
name: "Test",
data() {
return {
}
},
methods: {
allSon(){
//2、this.$refs 就是打印全部子组件了
console.log(this.$refs);
},
oneSon(){
//3、this.$refs.名字 就是打印某一个子组件的东西了
console.log(this.$refs.son1);
console.log(this.$refs.son1.message);
this.$refs.son1.showMessage()
}
},
components:{
TestSon
}
}
</script>
<style scoped>
</style>
点击按钮1
点击按钮2
2)子访问父
其实子访问父的很少的
①$parent
父
<template>
<div>
<TestSon></TestSon>
</div>
</template>
<script>
import TestSon from './testSon.vue'
export default {
name: "Test",
data() {
return {
message:'我是父组件的一句话'
}
},
components:{
TestSon
}
}
</script>
子
<template>
<div>
<button @click="fu">按钮</button>
</div>
</template>
<script>
export default {
name: "TestSon",
data() {
return {
message:'这是一句话'
}
},
methods: {
fu(){
console.log(this.$parent);
}
},
}
</script>
这是获取父的
②$root
获取根,最上级的那个
<template>
<div>
<button @click="fu">按钮</button>
</div>
</template>
<script>
export default {
name: "TestSon",
data() {
return {
message:'这是一句话'
}
},
methods: {
fu(){
console.log(this.$root);
}
},
}
</script>
12、全局变量localStorage存值、删值、获值
1)存值
//普通地
localStorage.setItem("下标",值);
localStorage.setItem("holiday_start_date",that.holiday_time[0]);
//如果值是对象的话,先转换
localStorage.setItem("下标",JSON.stringify(值));
localStorage.setItem("holiday_emp",JSON.stringify(response.data.data[0]));
2)获值
//普通
localStorage.getItem("下标")
localStorage.getItem("is_week")
//对象,
var xx = (JSON.parse(localStorage.getItem("下标")))
var id = (JSON.parse(localStorage.getItem("holiday_emp"))).id;
3)删
localStorage.removeItem("下标")
13、创建对象
setUserInfo(mobile){
var that = this
var user = {mobile:mobile}
}
14、时间格式
数据库是datetime 的2021-03-27 15:25:39,页面显示的是1616858739000
npm install moment --save
main.js中添加
import Moment from 'moment'
// 定义全局时间戳过滤器
Vue.filter('formatDate', function(value) {
return Moment(value).format('YYYY-MM-DD HH:mm:ss')
})
然后在用的地方
<span class="el-icon-time">注册时间{{user.regdate | formatDate}}</span>
14.5、过滤器
14的时间格式书写就是用过滤器来写的,一般是这么写的
{{参数| 过滤器名}}
data: {
},
filters:{
过滤器名(参数){
return 一般是返回什么格式的
}
}
全局配置过滤器
不用每个过滤器都写在一个组件啦,全局配置一个,大家都能用
//main.js
import * as filters from './filters'
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
// src/filter/index.js
import Moment from 'moment'
import {trans_tp, payment_type} from '@/views/scenesPay/dict'
/**
* @param {日期} dateStr
* @param {格式化模式} pattern
*/
export function dateFormat(dateStr, pattern) {
if (!dateStr) return ''
return Moment(dateStr).format(pattern)
}
然后平时用就{{ a | dateFormat }}
这样使用
15、分页
<el-pagination
background
layout="prev, pager, next"
:total="hotListTotal"
page-size="5"
@current-change="pageHot">
</el-pagination>
- :total是总数,一般在查询的时候就加上去总数了
- page-size一页显示多少条数据
@current-change="pageHot"
点击页数的时候,触发的函数
首页加载时,创建这个函数
getNewProblemList(){
var that = this
axios.get('http://192.168.12.128:9012/qa/problem/newlist/1/5').then(function(response) {
if(response.data.flag==true){
//数据
that.$data.problemNewList = response.data.data.rows
//总有多少条数据
that.$data.newListTotal = response.data.data.total
}
console.log(response);
})
},
记得在data加上newListTotal 和problemNewList
到这里,获得了多少个函数和初始化了,接下来就是点击页数的时候,触发的函数
//当被点击页数的时候,跳转
pageHot(currPage){
var that=this;
axios.get('http://192.168.12.128:9012/qa/problem/hotlist/'+currPage+'/5').then(function (response) {
if(response.data.flag==true){
//数据覆盖
that.$data.problemHotList = response.data.data.rows
}
console.log(response);
})
},
- currPage是当前点击页数显示的数字
16、axios.delete和平时的不一样
这个B,这个参数和平时的不一样
平时是
var tempData={
userid:userid
}
axios.post('http://192.168.12.128:9012',tempData).then(function (response) {
console.log(response);
})
而delete是
var tempData = {
userid:JSON.parse(localStorage.getItem("userInfo")).id,
targetuser:targetuser
}
axios.delete('http://192.168.12.128:9012/user/user/follow',{data:tempData}).then(function(response) {
if(response.data.flag==true){
// that.$message("删除成功")
}
that.$message(response.data.message)
console.log(response);
})
17、路由占位符,组件里面获得数据
就比如
<router-link :to="'/user/'+problem.userid">提出
一个跳转这里使用动态id,比如problem.userid
值为5
,就会跳转到/user/5
,而跳到那个页面,怎么获取这个5呢?
1、路由
先在路由写
{
path:'/user/:id' ,
name:'UserItem',
component:UserItem
}
:id
是一个占位符
2、组件获取数据
<template>
<div>
{{userid}}
</div>
</template>
<script>
export default {
name: "",
data() {
return {
userid:''
}
},
methods: {
},
created() {
this.userid = this.$route.params.id
}
}
</script>
<style scoped>
</style>
18、级联选择器
级联选择器
其数据是这样的
options: [
{
text: '浙江省',
value: '330000',
children: [{ text: '杭州市', value: '330100' }
{ text: '宁波市', value: '330100' },
{ text: '温州市', value: '330100' }],
},
{
text: '江苏省',
value: '320000',
children: [{ text: '南京市', value: '320100' }],
},
],
就是children嘛,如果有children(不管是不是空)就继续点下一级,没有就返回数据。但是目前一半返回的数据中,就算最后一级了,也是会有个children:[]
的,比如
[
{
"id": "441900117000",
"findname": "凤岗镇",
"find_quanpin": "fenggangzhen",
"find_jianpin": "fgz",
"province": "广东省",
"displayorder": 441900,
"netname": "gdwebson.gdnyt.com",
"netaddress": "1.1.1.1",
"deptarttype": 1,
"departtype": 1,
"tag": "东莞市 , 广东省",
"children": [
{
"id": "901120000000401",
"findname": "东莞凤岗站",
"find_quanpin": "dongguanfenggangzhan",
"find_jianpin": "dgfgz",
"province": "广东省",
"displayorder": null,
"netname": "gdwebson.gdnyt.com",
"netaddress": "1.1.1.1",
"deptarttype": 2,
"departtype": 2,
"tag": null,
"children": [],
"address": "东莞市凤深大道与龙平西路交汇处东北角",
"stationhotline": "0769-87513786"
}
],
"address": null,
"stationhotline": null
}
]
就算children没有数据,也是有这个字段的。如果只要还有children字段,就会显示下一级,但是下一级是空的没有得选
这个时候就要去掉最后一级得children字段
// 递归判断列表,把最后的children设为undefined
getTreeData(data) {
for (var i = 0; i < data.length; i++) {
if (data[i].children.length < 1) {
// children若为空数组,则将children设为undefined
data[i].children= undefined;
} else {
// children若不为空数组,则继续 递归调用 本方法
this.getTreeData(data[i].children);
}
}
return data;
}
19、:class绑定的几种方式
或者一个标签几个动态的class,可以这样写
20、计算属性
简单形式
之前没遇到过的,比如有firstName和lastName两个属性,在页面上我们要
{{firstName}} + {{lastName}}
这样显示,就很麻烦
有个计算属性的东西,可以自动拼接字符的
{{fullName}}
<script>
xxxx省略代码
data:{
firstName:'jack',
lastName:'white'
},
computed:{
fullName:function(){
return this.firstName + ' ' + lastName
}
}
</script>
data里面没有fullName,但是在computed里面显示了,页面显示jack white
值得一提的是,data的值改变,其computed也是会重新计算一次的
get形式
其实我们这是缩写,其实本来是这样的
{{fullName}}
<script>
xxxx省略代码
data:{
firstName:'jack',
lastName:'white'
},
computed:{
//这里如果直接fullName :function(){},就是只取get方法
fullName:{
get:function(){
return this.firstName + ' ' + lastName
}
}
</script>
其实也有set的
老面向对象了,也是有set的。比如你什么button按钮想改变值,也是可以的
{{fullName}}
<script>
xxxx省略代码
data:{
firstName:'jack',
lastName:'white'
},
computed:{
//这里如果直接fullName :function(){},就是只取get方法
fullName:{
get:function(){
return this.firstName + ' ' + lastName
},
set:function(newValue){
//这里把值给保存给data了
const names = newValue.spit(' ');
this.firstName = names[0];
this.lastName = names[1];
}
}
</script>
21{{}}里面直接写函数也是可以的
{{getFullName()}}
<script>
xxxx省略代码
data:{
firstName:'jack',
lastName:'white'
},
methods:{
getFullName(){
return this.firstName + ' ' + lastName
}
}
</script>
22、各个修饰符
@keyup.enter
就是触发回车的事件之类的
.stop修饰符
一个div有个点击事件,div里面有个button有个点击事件,那么点button的时候,会触发div点击事件和button点击事件。
那么我想点button只触发button而不触发div事件,可以@click.stop="xx"
23、.native
自定义组件的时候,直接@click="xx"
无反应,在自定义组件的时候应该加上@click.native="xx"
24、遍历对象
<ul v-for="(value,key,index) in person">{{value}}--{{key}}--{{index}}</ul>
//js
data() {
return {
person:{
name:'小强',
age:22
}
}
},
小强–name–0
22–age–1
25、函数的不定长参数
function sum(...num){
//code
}
sum(10,20,30,35)
26、vue中数组的一些自带方法
push
数组末插入数据
this.arr.push('aa')
pop
删除最尾的数据
this.arr.pop()
shift
删除数组最前面的一个元素
this.arr.shift()
unshift
再数组最前面添加一个元素
this.arr.unshift('aa')
splice
其有好几个作用
删除元素
this.arr.splice(从第几个开始删,删除几个元素)
//从第二个元素开始删除,删除2个 [a,b,c,d] --> [a,b]
this.arr.splice(2,2)
替换元素
this.arr.splice(从第几个开始替换,替换几个,替换的元素)
//从第二个元素开始替换,删除2个 [a,b,c,d] --> [a,b,e,f]
this.arr.splice(2,2,'e','f')
插入元素
this.arr.splice(第几个后面插入,0,插入的元素)
//从第二个元素后面插入 [a,b,c,d] --> [a,b,e,f,c,d]
this.arr.splice(2,0,'e','f')
27、js高阶函数
比如const nums = [10,20,111,112]
filter
filter中的参数是回调函数,其回调函数必须返回一个boolean值。返回值是数组。filter的回调函数会循环一遍数组的
用来过滤的,比如取出数组小于100的数字,普通的
let newNums = []
for(let n in nums){
if(n<100){
newNums.push(n)
}
}
但是可以这样简写
//用newNums来接住数组,然后filter里面写回调函数,return true 或者 false
let newNums = nums.filter(function(n){
return n < 100
})
map
和filter差不多,只不过filter规定返回布尔值,map的返回值不规定
比如要求每个数*2
let newNums = []
for(let n in nums){
newNums.push(n*2)
}
高阶
let newNums = nums.map(function(n){
return n*2
})
reduce
对数组进行汇总,他是这样的
数组.reduce(回调函数(上一次遍历的值,当前遍历的值){
// code
},初始值)
比如将所有数加起来
const nums = [10,20,111,112]
let total = 0
for(let n in nums){
total + =n
}
可以这样写
let total = newNums.reduce(function(preValue,n){
return preValue + n
},0)
第一次遍历,preValue = 0,n=10 , return 10
第二次遍历,preValue = 10,n=20 , return 30
第三次遍历,preValue = 30,n=111 , return 143
第三次遍历,preValue = 143,n=112 , return 255
28、checkbox多选框的使用
比如这个,选中哪个,就记录哪个。
之前我是每一个都弄一个点击事件,然后再push和pop操作的。
后来发现视频,不用的,直接,每一个input都是v-model绑定一个数组,然后直接在下面{{数组}}
就可
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="唱歌" v-model="hobbies">唱歌
<input type="checkbox" value="跳舞" v-model="hobbies">跳舞
<input type="checkbox" value="足球" v-model="hobbies">足球
<h2>爱好:{{hobbies}}</h2>
//------
data() {
return {
hobbies:[]
}
},
29、v-model修饰符
lazy
懒加载,由于v-model双向绑定,比如input还在输入的时候,data会时时改变
<input type="text" v-model.lazy="msg">
{{msg}}
//------
data() {
return {
msg:''
}
},
但我们想在input中输完时,按下回车或者焦点离开输入框,data值才改变
还没按回车,后面的值不会改变
按下回车,值改变
number
就是说input的时候,只能输入数字类型,如果是<input type="number">
data里面判断类型时string而不是number,这时候这样用就可以
<input type="number" v-model.number="msg">
{{typeof msg}}
//------
data() {
return {
msg:''
}
},
30、父传子,避免用v-model直接绑定props中的值
比如
父
<template>
//用了子组件,传一个key为id,value则为bookid的值,传给子组件读取
<UserData :id="bookid"></UserData>
</template>
<script>
import UserData from '../../components/UserData'
export default {
name: "FriendIndex",
components: {
//加载儿子
UserData
},
data(){
return{
bookid:123
}
}
<script>
子:
<template>
<div>
{{id}}
//这里用了父子
<input type="text" v-model="id">
</div>
</template>
<script>
export default {
name: "",
//这里的props为数组形式,不是一个数组形式,就是保存父给子的key,写在这里
props:["id"],
}
</script>
<style scoped>
</style>
v-model绑定了props而不是data,会有错误
官方建议data先拿到props,然后再对data进行双向绑定
修改后子:
<template>
<div>
{{reallyId}}
//这里用了父子
<input type="text" v-model="reallyId">
</div>
</template>
<script>
export default {
name: "",
//这里的props为数组形式,不是一个数组形式,就是保存父给子的key,写在这里
props:["id"],
data(){
return {
reallyId:this.id
}
}
}
</script>
<style scoped>
</style>
31、watch监听某一值的改变
和data同级,某一值发生改变时,执行的代码
<template>
<div>
<input type="text" v-model="reallyId">
</div>
</template>
<script>
export default {
name: "",
data(){
return {
reallyId:'123'
}
},
watch:{
//一个新值,一个旧值
reallyId(newValue,oldValue){
//要执行的代码
}
}
}
</script>
<style scoped>
</style>
32、插槽slot
插槽嘛,插入啥变变成啥
应用场景多为一个重复的组件,但是组件和组件之间又有些不一样的需求。有点类似于Java的集成,把大家共性的东西都打包,要怎么改怎么用,就是子类的事了。
导航条,左边有些事返回,有些是菜单,但是总的来说,大家都是左中右3个地方,我们就可以将这些共性打包
1)简单插槽
<!-- 子 -->
<template>
<div>
<!-- 这里都显示 -->
我是子组件
<!--1、 solt是插槽,要什么的就在这里插入 -->
<slot></slot>
<br><br>
</div>
</template>
<!-- 父 -->
<template>
<div>
<!-- 2、在子组件里面插入你想要插入的东西,这个东西就会在solt中替换的 -->
<TestSon><button>按钮</button></TestSon>
<!-- 3、没有插入的就啥事没有发生 -->
<TestSon></TestSon>
<!-- 4、或者你插入其他的 -->
<TestSon><i>我是i</i></TestSon>
<!-- 5、插入多个的话,会都加载,将多个都在solt中替换 -->
<TestSon>
<button>我是button</button>
<h2>我是h2</h2>
</TestSon>
</div>
</template>
3)具有默认值的插槽
如果调用10次插槽,8次要求是button,2次要求是h2,那么每一个我都要些button和h2,但是button是8次啊,明显又麻烦了,所以有默认值,和Java抽象方法的默认值这种差不多
<!-- 父 -->
<template>
<div>
<!-- 2、覆写默认值 -->
<TestSon><p>老子不想要按钮</p></TestSon>
<!-- 3、空白就直接用默认值 -->
<TestSon></TestSon>
<TestSon><button>老子是自己写的按钮</button></TestSon>
</div>
</template>
<!-- 子 -->
<template>
<div>
我是子组件
<!-- 1、定义了一个solt,但有默认值 -->
<slot><button>按钮</button></slot>
<br><br>
</div>
</template>
3)具名插槽
上面是子组件里面只有一个solt,但是如果想几个solt怎么办?
<!-- 子 -->
<template>
<div>
<!-- 写3个插槽 -->
<slot>左边</slot>
<slot>中间</slot>
<slot>右边</slot>
<br><br>
</div>
</template>
<!-- 父 -->
<template>
<div>
<TestSon></TestSon>
<!-- button覆盖3个插槽 -->
<TestSon><button>按钮</button></TestSon>
</div>
</template>
如果我们只想替换中间那个,可以这样写
<!-- 子 -->
<template>
<div>
<!-- 1、给每一个插槽起名字-->
<slot name="left">左边</slot>
<slot name="center">中间</slot>
<slot name="right">右边</slot>
<br><br>
</div>
</template>
<!-- 父 -->
<template>
<div>
<TestSon></TestSon>
<TestSon>
<!-- 在子组件里面,插入template ,属性v-solt:插槽名 ,然后再在button里面插入想要的东西-->
<template v-slot:center>
<button>按钮</button>
</template>
</TestSon>
</div>
</template>
v-slot 只能添加在 <template>
中
4)作用域插槽
子组件的插槽的slot中的样式是子组件写的,但是,如果我想用到子组件插槽中的数据,但是只是想改变样式而已。
就是说,比如子组件默认插槽值是
但是我想换成这样子
其中,这些值是子组件的值,这时候如果要改变样式并且拿到子组件的值,要这样做
<!-- 子 -->
<template>
<div>
//2、把值给父组件读取,需要加上 :自定义名字="变量" ,这样父组件通过名字就能读取了
<slot :language="language">
//1、这里是slot中的默认样式
<ul>
<li v-for="item in language"> {{item}} </li>
</ul>
</slot>
<br><br>
</div>
</template>
<script>
export default {
name: "TestSon",
data() {
return {
language:['java','php','js']
}
},
</script>
<!-- 父 -->
<template>
<div>
<TestSon></TestSon>
<TestSon>
//3、v-slot:default="slotProps" ,这里的 slotProps就可以拿到子组件插槽中的所有变量(名字也是自己起的)
<template v-slot:default="slotProps">
//4、这里读slotProps,会拿到全部值
<div>slotProps: <h2>{{slotProps}}</h2>
</div>
<br>
//5、slotProps.什么 ,就是子组件插槽中定义的值
<div>slotProps.language: <h2>{{slotProps.language}}</h2>
</div>
<br>
//6、遍历
<span v-for="item in slotProps.language">{{item}}-</span>
</template>
</TestSon>
</div>
</template>
另一个例子
33、js的模块化导出导入
js是弱语言,没有Java这么强的封装性和继承性。
如果正常导入js文件,是<script src="xx.js"></script>
,那么如果导入的几个js文件有重名变量,函数,会覆盖。
那么模块化就是把每个模块之间隔绝,重名变量就不会覆盖。
导出就是模块隔绝后,开个接口给别人用,就是导出export
导入就是导入别的模块来使用,导入import
简单的模块化
js就写一些普通的函数,或者变量,别人用就要这样用
<script src="main.js" type="module"></script>
简单的导入导出
//a.js,定义一个变量和函数
// 1、写东西
var flag = true
function sum(num1, num2) {
return num1 + num2
}
//2、导出
export {
flag,sum
}
//b.js导入a.js,并调用东西
//3、导入js
import {flag,sum} from './a.js'
//4、引用变量和函数
if(flag){
console.log(sum(10,20))
}
导出方式
1)写完再导出
var flag = true
function sum(num1, num2) {
return num1 + num2
}
//2、导出
export {
flag,sum
}
2)边写边导出
export var flag = true
export function sum(num1, num2) {
return num1 + num2
}
3)导出函数
export class Person(){
run(){
console.log('跑')
}
}
4)export default
上面的导入,都是import {xx} from 'x.js'
,但是平时vue的都是import 自己起名字 from 'a'
那么这个自己起名字就是通过export的
//a.js
var num =666
export default num
import shuzi from './a.js'
console.log(shuzi)
666
export default一个文件只能存在一个
导入方式
1)一个个导入
//a.js,定义一个变量和函数
var flag = true
var num = 123
function sum(num1, num2) {
return num1 + num2
}
//2、导出
export {
flag,sum,num
}
//b.js导入a.js,并调用东西
import {flag,sum,num} from './a.js'
//4、引用变量和函数
if(flag){
console.log(sum(10,20))
}
2)一起导入
//b.js导入a.js,并调用东西
import * as obj from './a.js'
//4、引用变量和函数
if(obj.flag){
console.log(obj.sum(10,20))
}
34、webpack打包JS
webpack xxx.js xx.js
35、简单理解npm
npm是一个maven管理器,控制一堆包的,但是npm不像maven那么高级,所有包都放在一个仓库,他
是每一个项目都有自己的仓库,叫node_modules。
npm install xx -g
保存到全局,但有些东西是每个项目都要单独安装的,仅适用于类似webpack这种全局的打包的
36、脚手架创建文件没有webpack.config.js
https://blog.csdn.net/zz00008888/article/details/109536466
vue cli2.x才会有webpack.config.js,3.x之后就类似spring boot的自动配置一样,要加的话往里面加个vue.config.js进行修改即可
37、npm install错误
目前只知道几种解决方案
1)npm clean cache --force
注意在cmd用管理员身份打开
其其实就是把C:\Users\master\AppData\Roaming\npm-cache删除,清下缓存
38、咋看vue版本
网上说的 vue -V
看的是cli版本的!!!
首先vue版本准确说是怎么看项目的vue版本,打开项目的node_modules\vue\package.json
,查看版本
可以看到我用的是vue2.x而不是3.x
emmmm
我一直以为自己用3.x了。。。。。
39、箭头函数
其实就是Java8的lambda
const obj = {
sum(num1,num2){
return num1+num2
}
}
const test = function (str){
console.log(str);
}
test(666)
obj.sum(10,20)
简写
const sum = (num1,num2) =>{
return num1+num2
}
sum(10,20)
如果函数里面,只有一个参数可以
then((data) => {
console.log(data);
then(data => {
console.log(data);
40、url的hash和history
hash方式
比如在https://www.jd.com/
输入location.hash='/foo'
,就到了https://www.jd.com/#/foo
但是页面没有刷新,而且也有个后退按钮
当输入history.back()
时,又退回到了https://www.jd.com/
其实这玩意就是个栈,请求一层层往上加,back就去掉一层
history方式
history.pushState({},'','home')
history.go()
history.back() == history.go(-1)
history.go(-2)就回退2个
如果是history.go(1)就返回刚才的页面,其实就是这个
41、深入浅出路由
之前都是回用就好了,现在回顾一下
router\index.js
import Vue from 'vue'
//1、先导入插件
import VueRouter from 'vue-router'
import Index from '../views/Index.vue'
//2、插件的使用方式,再使用Vue.use(xxx)
Vue.use(VueRouter)
//4、routes定义在router里面的数组
const routes = [
{
path: '/',
name: 'Index',
component: Index
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
//3、定义一个变量,3个选项嘛,最后一个变量是数组
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
//5、最后面要导出啦
export default router
再到根目录的main.js
import Vue from 'vue'
import './plugins/axios'
import App from './App.vue'
//导入那个 ./router/index.js
import router from './router'
import store from './store'
import './plugins/element.js'
Vue.config.productionTip = false
//这里就是new Vue了,有个router,就是简写的 router:router
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
42、路由懒加载
其实就是
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
或者是
const About = () => import(/* webpackChunkName: "about" */ '../views/About.vue')
{
path: '/about',
name: 'About',
component: About
}
好处是,这样打包后,加载速度会更块,以后都推荐这种方式
43、导航守卫
就是一个router里面的东西,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航,有点拦截器的感觉,可以配合安全验证之类的进行拦截跳转。
官网介绍:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB
比如我们想根据url显示不同的title(就是浏览器标签页中图标右边的文字)
1)在router/main.js添加meta
{
path:'/test',
name:'Test',
component:Test,
meta:{
title:'测试1'
}
},{
path:'/test2',
name:'Test2',
component:Test2,
meta:{
title:'测试2'
}
}
meta是数据的描述注释之类的,除了title还有其他很多的。
2)router/main.js 里面添加全局前置守卫
//beforeEach是前置守卫,在跳转前执行的函数,其有3个变量,from跳转到to,然后next是个函数,可以中断,可以继续跳转
router.beforeEach((to, from, next) => {
//设置标题
window.document.title = to.meta.title
//可以看看有啥
console.log(to);
//next一定要有,无的话会报错
next()
})
打印to可以看到有很多信息,之后我们甚至可以判断路径url显示自定义的东西之类的,
如果有时候判断不了,例如子子组件,可以用matched来解决子的路径
3)next
-
next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
-
next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
-
next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
就是说,我们可以判断有没有登陆之类的,没有登陆就next(‘/login’)这种
4)后置守卫
router.afterEach((to, from) => {
// ...
})
就是后置啦,没有next
5)路由独享的守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
6)组件内的导航守卫
//和data,created同级
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
44、keep-alive的使用
为社么要用keep-alive
因为一个页面,有created和destory两个函数嘛,组件创建和毁灭,当从A到B时,A毁灭B创建。但我后退一步,又要B毁灭再A创建。这样很麻烦,虚拟dom弄了好多次,而且如果A到B再到A,那么A原本的东西就会都不见了,因为A已经被毁灭过了,保存的临时数据页销毁了。keep-alive就是解决这个问题得到。
案例
这个案例原本可以简单点的,但是这个问题公司也会用到,一起讲了
比如我们页面是这样
跳转到到home时,识别或者重定向跳转到/home/toutiao
home有两个子:头条和新闻,和home同级的有my
当点击home时,或者点击头条时,跳转到home/toutiao
点击新闻时跳转到home/news
点击my时跳转到/my
当点击home的新闻,再点击my,再点击到home的时候,如果是使用重定向,会跳转到home/toutiao,那么我刚才浏览的明明是新闻,还要继续点一个新闻才到新闻。我想在home/news时,点my,再点home,会跳到home/news而不是home/toutiao
点my=》 点home=》
可以这样做
首先路由
{
path: '/home',
name: 'home',
component: home,
redirect :'/home/toutiao',
children: [{
path: 'news',
name: 'news',
component: news,
}, {
path: 'toutiao',
name: 'toutiao',
component: toutiao,
}]
}, {
path: '/my',
name: 'my',
component: my
},
//app.vue
<template>
<div>
//根目录就2个按钮
<button @click="tohome">home</button>
<button @click="tomy">my</button>
//1、加了keep-alive就行
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
methods: {
tohome() {
this.$router.push('/home')
},
tomy() {
this.$router.push('/my')
}
}
}
</script>
//home.js
<template>
<div>
我是home <br>
//到了home里面,有2个按钮,根据按钮在router-view显示对应
<el-button @click="totoutiao"> 头条</el-button>
<el-button @click="tonews"> 新闻</el-button>
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
data() {
return {
//1、先定义path,这样就类似重定向
path: '/home/toutiao'
}
},
methods: {
totoutiao() {
this.$router.push('/home/toutiao')
},
tonews() {
this.$router.push('/home/news')
}
},
//2、在keep-alive里面才有这个函数,表示当这个页面活跃的时候,
//从home/new跳转到my的时候,home不会被销毁,而且记得了上一次呆的地方是news,所以再点击home时,执行activated跳到home/news
activated() {
this.$router.push(this.path)
},
//组件内后置守卫,跳转了之后的
beforeRouteLeave(to, from, next) {
console.log(this.$route.path)
//要离开了,记得这次是从哪里离开的,方便再次加入
this.path = this.$route.path
next()
}
}
</script>
其实说那么多就是为了表达keep-alive是可以方式组件再次被创建和销毁的,这个案例我觉得工作会用到所以记下的
注意点
我们先看下生命周期
created和mounted,然后接下来就是平常的东西,如果或数据发生变化,发生updated,然后销毁destoryed。
keep-alive有2个函数,activated和deactivated ,一个是某个组件活跃的时候(用到的时候),另一个就是组件不活跃(没用到的时候)
不让某个节点有keep-alive
比如详情页之类得,点击详情页再返回,再点击其他的详情页,因为用了keep-alive,不会再执行mouth和created,所以这个时候需要排除某些组件
<keep-alive exclude="xxx组件名">
</keep-alive>
45、为啥url有时候有#
这是因为VueRouter设置的模式是hash模式,设置成history就不是#了
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
判断什么url干什么事
比如,tabbar的,根据url为/index的时候,首页按钮显示红色,其他都是黑色
就是设置2个,活跃的时候,显示红色,不活跃显示黑色,这个活跃值就这样判断
if(this.$route.path.indexOf('/home') !==-1){
//code...
}
str.indexOf('key')
:判断str中是否包含key,没有则返回-1,有则返回第几个开头的字
小实例?
created() {
console.log(this.$route);
if(this.$route.path.indexOf('/home/toutiao') !==-1){
console.log('trueeeeee!')
}
},
46、配置路径别名
如果导入一些图片之类的,如果文件移动了,什么…/,…/…/之类的很多,取个别名就好多了
默认@就是src,比如src/main.js,就是@/main.js
其他更多的可以自定义设置名字
//vue.config.js
const path = require('path'); //引入path模块
function resolve(dir){
return path.join(__dirname,dir) //path.join(__dirname)设置绝对路径
}
module.exports={
chainWebpack:(config)=>{
config.resolve.alias
//set第一个参数:设置的别名,第二个参数:设置的路径
.set('@',resolve('./src'))
.set('assets',resolve('.src/assets'))
.set('components',resolve('./src/components'))
.set('views',resolve('src/views'))
}
}
其他用view,就
//import就这样写
import xx from 'views/xxx'
//如果非import,前面要加~
<img src="~xxx" alt="">
47、Promise
这是一个异步编程的一种解决方案
其实我之前敲代码也有过,比如我axios时,发送一个请求,当拿到结果后,再从拿到的结果再次发送一个请求。或许没啥事,但是当时写得是挺乱得,如果以后还有套娃嵌套娃嵌套娃,那就很恶心了,而promise就是为了让代码更好看,而且执行效率高,结构分明的东西。
Promise主要是用于有异步处理的地方
其大概结构是这样写的
new Promise((resolve, reject) => {
//...code
//成功时执行resolve函数,实际上就是执行then的函数
resolve('success');
//失败时执行reject函数,实际上就是执行catch的函数
reject('err')
}).then((data) => {
//成功之后的事
console.log(data);
}).catch((err) => {
//失败之后的事
console.log(err);
})
- Promise的参数为一个函数,那个函数又有2变量resolve和reject,但resolve和reject也都是函数(不用写具体内容)。于是就可以用箭头函数简写啦
- 其在里面进行操作异步处理,在执行成功的地方写入resolve函数传参。就会自动跳到then执行代码。在执行失败的地方写入resolve函数传参,就会自动跳到catch执行代码
或者是
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve')
reject('reject')
}, 1000);
}).then(data=>{
//执行成功的代码
console.log(data);
},err=>{
//执行错误的代码
console.log(err);
})
//其其实就是then(function(data),function(err))
//第一个执行成功的代码,第二个执行失败的代码
小案例
比如我们这有个套娃的,用setTimeout模仿异步请求啦
一个setTimeout里面,执行代码,然后执行完里面又有个setTimeout,执行代码,里面又有setTimeout
setTimeout(() => {
console.log('11111');
setTimeout(() => {
console.log('222222');
setTimeout(() => {
console.log('333333');
}, 1000);
}, 1000);
}, 1000);
打印是可以打印,就是有点恶心。用Promise优雅一下
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1111')
reject('err')
}, 1000);
//执行第一层成功代码
}).then((data) => {
console.log(data);
//code..
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('2222')
reject('err')
}, 1000);
//执行第二次成功代码
}).then((data) => {
console.log(data);
//code...
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('3333')
reject('err')
}, 1000);
//执行第3次成功代码
}).then((data) => {
console.log(data);
//执行第3次失败代码
}).catch((data) => {
console.log(err);
})
//执行第二次失败代码
}).catch((data) => {
console.log(data);
})
//处理第一层的错误代码
}).catch((data) => {
console.log(data);
})
虽然看起来还是恶心,但是,如果代码逻辑很复杂,这个就好
链式调用
还有链式调用,还有链式调用的简写方式,但我实在学不懂了,简写以后再学吧
all方法
我觉得比较罕见,就是2个异步任务,要任务A成功,任务B成功,2个一起成功了才执行自定义代码
//all里面是个数组,每一个元素都是new一个Promise对象
Promise.all([
new Promise((resolve, reject) => {
//code
}),
new Promise((resolve, reject) => {
//code
})
//几个异步任务完成后,都是成功执行后,再来到then函数
//其中,result是一个数组,result[0]是第一个异步任务完成的回调数据,result[1]是第二个异步任务完成的回调数据,
]).then(result => {
console.log(result);
})
比如
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
name: '小强',
age: 18
})
}, 1000);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
name: '小明',
age: 22
})
}, 1000);
})
]).then(result => {
console.log(result);
})
结果为:
[
{
"name": "小强",
"age": 18
},
{
"name": "小明",
"age": 22
}
]
48、导入插件 或者 创建自己的工具类和插件
就是用别人的插件之类的
目前我只会怎么用别人的插件,创建自己的插件(工具类)我还不会
使用别人的插件
用官方的vuex为例子,其他第三方插件我不知道
主要是我查不到正确的做法是什么,官方这样导入的,其他应该或许大概也是这样做的?
- 先下载插件(npm install xxx)
- 新创个文件,专门做这个插件的事情
// store/index.js //1、导入vue和vuex import Vue from 'vue' import Vuex from 'vuex' //2、使用插件 Vue.use(插件) Vue.use(Vuex) //3、导出插件,export default 插件对象 //可以是new xx直接写 //也可以是const x = new 插件对象,写好再export default x export default new Vuex.Store({ state: { }, mutations: { }, actions: { }, modules: { } })
- 再在main.js中使用插件
然后其他的也能通过import Vue from 'vue' //4、导入插件 import store from './store' Vue.config.productionTip = false new Vue({ //5、在这里写,这是官方的,我不知道第三方插件在new Vue里面,还是Vue.use store, render: h => h(App) }).$mount('#app')
$store
使用
49、vuex
1)简单使用
就是之前遇到的,2个毫无相关的组件,要用同一个变量。之前是通过全局变量localStorage来实现的,但这个不是响应式的,不好做,查了下发现原来vuex就是做全局变量的,而且是响应式的。
vuex在store/index.js下
要的全局变量就在state里面加,比如
//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
//设置全局变量
count:1000
},
mutations: {
},
actions: {
},
modules: {
}
})
别人用的话
我是全局变量counter:{{this.$store.state.count}}
就可
我是全局变量counter:1000
2)修改vuex中的数据
既然知道vuex是进行全局变量的,那么肯定能读能写,读就直接this.$store.state.变量名
去读,但是写的话不可以通过传统的this.$store.state.变量名 ++
,因为不单单你一个组件在写,其他组件也可能会写,这样直接改会发生异步错误处理。vuex都帮我们做好了
//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:1000
},
mutations: {
//1、vuex定义一个方法,为count++,参数加state
increment(state){
state.count++
},
//vuex定义一个方法,为count--
decrement(state){
state.count--
},
//还可以传参,第一个是state,第二个就是传参数据,如果是多个传参数据,可以用对象传,再 对象.xx
incrementNum(state, num) {
state.count += num
}
},
actions: {
},
modules: {
}
})
<template>
<div>
我是全局变量counter:{{this.$store.state.count}}
<!-- 2、普通的点击事件 -->
<button @click="add">+</button>
<button @click="jian">-</button>
<button @click="addNum(5)">+5</button>
<button @click="addNum(10)">+10</button>
</div>
</template>
<script>
export default {
name: "",
methods: {
add(){
//3、事件里面 this.$store.commit('vuex中方法名') 来进行值得修改
this.$store.commit('increment')
},
jian(){
this.$store.commit('decrement')
},
addNum(num){
this.$store.commit('incrementNum', num)
},
},
}
</script>
这样子就可以改,而且可以在插件里面看到值得修改过程
3)getters
这类似于普通组件的computed,什么计算属性,可以写一些tostring的东西
//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 1000,
array: [10, 20, 30, 40]
},
mutations: {
},
getters: {
//重构,显示对应的东西
toStringCount(state) {
return 'count:' + state.count
},
//可以配合过滤
more20(state) {
return state.array.filter(n => {
return n > 20
})
},
//参数可以加getters,调用getters的方法
more20Lent(state, getters) {
return getters.more20.length
},
//甚至可以自定义传参
moreNum(state) {
//return 回调函数,参数num就是要大于的数
return num => {
//然后过滤
return state.array.filter(n => {
//得到的数过滤回来
return n > num
})
}
}
},
actions: {},
modules: {}
})
<!-- 直接显示 -->
<h2>{{this.$store.getters.toStringCount}}</h2>
<h2>{{this.$store.getters.more20}} </h2>
<h2>{{this.$store.getters.more20Lent}} </h2>
<h2>{{this.$store.getters.moreNum(10)}} </h2>
3.5)getters还有更方便的读写方式
const getters = {
sid: state => state.user.sid
}
export default getters
const store = new Vuex.Store({
getters
})
然后在用的地方
import {mapGetters} from "vuex";
computed: {
...mapGetters(['sid']),
//其是会自动转换成
// sid(){
// return this.$store.state.sid
// }
},
然后就能简单地使用啦
console.log(this.sid)
4)特殊的mutations提交风格
上面的是普通的,下面这个特殊的
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 1000,
},
mutations: {
//这种接受的是一个对象
incrementNum(state, payload) {
console.log(payload);
state.count += payload.num
}
},
})
<button @click="addNum(5)">+5</button>
<button @click="addNum(10)">+10</button>
<script>
//方法
addNum(num){
//type和一个传值
this.$store.commit({
type :'incrementNum',
num
})
},
</script>
按下+10
{
"type": "incrementNum",
"num": 10
}
5)Mutation响应规则
2中是介绍了vuex中修改数据,但那仅限于修改,增加和删除我们都没讲
比如我们要修改和删除,先看错误的.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
obj: {
name: '小强',
age: '23'
}
},
mutations: {
updateObj(state) {
//obj增加一个key
state.obj['address'] = '深圳'
//obj删除一个key
delete state.obj.age
}
},
})
<h2>{{this.$store.state.obj}}</h2>
<button @click="changeObj">修改对象</button>
changeObj(){
this.$store.commit('updateObj')
}
点击修改对象按钮,也没效果
这么看来是已经修改了,但可惜的是,这2者不支持响应式布局(下面会讲哪些不是响应式的)
应该这么改
updateObj(state) {
// state.obj['address'] = '深圳'
// delete state.obj.age
//用这2个进行添加和修改
Vue.set(state.obj,'address','深圳')
Vue.delete(state.obj,'age')
}
6)Mutation常量类型
就是,我们commit传的是字符串嘛,为了统一性,用常量来定义这个字符串,然后传入这个常量即可
//mutations_types.js
export const INCREMENT = 'increment'
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
//导入
import * as type from './mutations_types.js'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 1000,
array: [10, 20, 30, 40],
obj: {
name: '小强',
age: '23'
}
},
mutations: {
//vuex定义一个方法,为count++
// increment(state) {
// state.count++
// },
//将名字和替换成常量
[type.INCREMENT](state) {
state.count++
},
},
})
<button @click="add">+</button>
import * as type from '../store/mutations_types.js'
add() {
// this.$store.commit('increment')
this.$store.commit(type.INCREMENT)
},
7)Action
上面的操作都是在mutation中进行的同步操作,但是很多情况是要异步操作的,而mutation不支持异步操作的,需要配合action使用。所以需要异步操作就方法->action->mutation
,不需要异步就方法->mutation
mutations: {
//在action中异步处理完后,在mutations中修改全局变量
muAsync(state,payload){
state.count +=payload.count
}
},
actions: {
//2、在action中,定义异步操作,在异步操作成功时context.commit到mutatuions
acAsync(context,payload){
setTimeout(() => {
context.commit('muAsync',payload)
}, 1000);
}
},
<h2>------------------</h2>
<button @click="async(2)">异步操作+2按钮</button>
{{this.$store.state.count}}
//js
//1、在方法里面进行异步修改全局变量,dispatch跳转到action,第二个参数是对象
async(num){
this.$store.dispatch('acAsync',{count:num})
}
ps
如果actions是在某个模块里面的,比如是
export default {
actions:{
setUserId ({ commit }, userId) {
commit('SET_USER_ID', userId)
},
}
}
//store.index.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)
// 文件加载
const modulesFiles = require.context('./modules', true, /\.js$/)
// 遍历文件导出store对象
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
getters
})
export default store
其他地方可直接用store.dispatch('user/setUserId', userId)
①结合Promise进行回调处理
就是在方法中,this.$store.dispatch('acAsync',{count:num})
成功完成后,进行的一些操作
actions: {
acAsync(context, payload) {
//返回一个promise,这样子,方法就可以继续写then了
return new Promise((resolve, reject) => {
setTimeout(() => {
//commit到mutations
context.commit('muAsync', payload)
//成功
resolve('success')
}, 1000);
})
}
},
methods: {
async (num) {
this.$store.dispatch('acAsync', {
count: num
//写回调
}).then(res => {
console.log(res);
})
}
}
8)Module模块化
因为什么都往里面塞的话,这个index.js就会很大不方便,有个模块化
const Person ={
state:{},
mutations:{},
getters:{},
actions:{}
}
export default new Vuex.Store({
state: {
count: 1000,
array: [10, 20, 30, 40],
obj: {
name: '小强',
age: '23'
}
},
mutations: {},
getters: {},
actions: {},
modules: {
//其实就是Person:Person的简写
Person
}
})
①使用
import Vue from 'vue'
import Vuex from 'vuex'
import * as type from './mutations_types.js'
Vue.use(Vuex)
const Person ={
state:{
name:'詹姆斯'
},
mutations:{
//注意这里的名字不要和根或其他任何模块重名
updateName(state,payload){
//state是本模块的state
state.name = payload.name
}
},
getters:{
//普通的getters
fullname1(state){
return state.name + '1'
},
//调用本模块的getters
fullname2(state,getters){
return getters.fullname1 + '2'
},
//还可以用根的state数据
fullname3(state,getters,rootState){
return getters.fullname2 + rootState.count
}
},
actions:{
//异步操作也是一样
syncUpdateName(context,payload){
console.log(context,payload);
setTimeout(() => {
context.commit('updateName',payload)
}, 1000);
}
}
}
export default new Vuex.Store({
state: {
count: 1000,
array: [10, 20, 30, 40],
obj: {
name: '小强',
age: '23'
}
},
mutations: {},
getters: {},
actions: {},
modules: {
//其实就是Person:Person的简写
Person
}
})
点击异步改变名字后
9)vuex项目结构
我们希望进行模块化的目录结构
store下,index是总,然后actions、mutations就管理总的action之类的,然后一个modules文件夹,管理各个模块,一个模块就有自己的action、mutations之类的
10)总结
vuex是个很好的全局变量管家,就是用起来有点麻烦。
要先预设什么全局变量,就在state中设置,
同步请求就在mutation中写好,方法再调用mutations
异步请求就方法调用action在action中进行异步处理,得到结果后再给mutations进行全局变量的修改
所有的全局变量修改一定要通过mutations进行修改。绕过mutations修改数据官方不建议,会造成数据错误
50、axios
1)crud
then和catch是每个都会有的
axios.request(config)
,其他的比如get:axios.request(url[,config])
①get
两者用的时候,都是在方法里面用的,格式为
axios.get('路径').then(res => {
console.log(res );
}).catch(err =>{
console.log(err);
})
②post
axios.post('路径',json类型的数据).then(res => {
console.log(res );
})
或者
axios.post('路径',{params:json类型对象}).then(res => {
console.log(res );
})
③put
axios.put('路径',json类型的数据).then(res => {
console.log(res );
})
④delete
axios.delete('路径',{data:json类型数据}).then(res => {
console.log(res );
})
2)all并发请求
//返回的是一个数组
axios.all([axios.get('url1'), axios.get('url2')])
.then(res => {
console.log(res);
})
//或者一个个地返回,推荐这种
axios.all([axios.get('url1'), axios.get('url2')])
.then(axios.spread((res1, res2) => {
console.log(res1);
console.log(res2);
}))
3)axios的配置信息
一般弄完基本的东西,有特殊的可以在里面加一些配置
①直接在某一个axios请求里面加进去
axios.all([
axios({
//config
baseURL: 'http://192.168.12.128:9012',
url: 'user/user/follow'
}),
axios({
//config
baseURL: 'http://192.168.12.128:9012',
url: 'user/user/follow',
params: {
type: 'all'
}
}),
])
.then(axios.spread((res1, res2) => {
console.log(res1);
console.log(res2);
}))
②全局的配置
//公共baseURL抽出来
axios.default.baseURL = 'http://192.168.12.128:9012'
axios.all([
axios({
url: 'user/user/follow'
}),
axios({
url: 'user/user/follow',
params: {
type: 'all'
}
}),
])
.then(axios.spread((res1, res2) => {
console.log(res1);
console.log(res2);
}))
③配置表
https://github.com/axios/axios
{
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法
method: 'get', // default
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `transformRequest` 允许在向服务器发送前,修改请求数据
// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
transformRequest: [function (data, headers) {
// 对 data 进行任意转换处理
return data;
}],
// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// `headers` 是即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` 是即将与请求一起发送的 URL 参数
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
// `paramsSerializer` 是一个负责 `params` 序列化的函数
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` 是作为请求主体被发送的数据
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时,必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // default
// `adapter` 允许自定义处理请求,以使测试更轻松
// 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
adapter: function (config) {
/* ... */
},
// `auth` 表示应该使用 HTTP 基础验证,并提供凭据
// 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // default
// `responseEncoding` indicates encoding to use for decoding responses
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // default
// `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
// `onUploadProgress` 允许为上传处理进度事件
onUploadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
},
// `onDownloadProgress` 允许为下载处理进度事件
onDownloadProgress: function (progressEvent) {
// 对原生进度事件的处理
},
// `maxContentLength` 定义允许的响应内容的最大尺寸
maxContentLength: 2000,
// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
// `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
// 如果设置为0,将不会 follow 任何重定向
maxRedirects: 5, // default
// `socketPath` defines a UNIX Socket to be used in node.js.
// e.g. '/var/run/docker.sock' to send requests to the docker daemon.
// Only either `socketPath` or `proxy` can be specified.
// If both are specified, `socketPath` is used.
socketPath: null, // default
// `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
// `keepAlive` 默认没有启用
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// 'proxy' 定义代理服务器的主机名称和端口
// `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
// 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// `cancelToken` 指定用于取消请求的 cancel token
// (查看后面的 Cancellation 这节了解更多)
cancelToken: new CancelToken(function (cancel) {
})
}
4)实例封装
如果一个文件写默认配置baseURL =xxx,那么会影响全部,但往往很多时候需要从不同的数据库拿不同的数据,那baseURL就不一定了
一种是,利用nginx反向代理,或者是spring cloudbus的那个,将所有请求统一起来再分发下去
另一种就是自己封装一层axiox实例,我们一般是用这个方法的
//request.js
//传入config,config会自动覆盖的
export function request(config){
//有个axios.create专门用于创建实例
const instance = axios.create({
baseURL:'http://192.168.12.128:9012',
timeout:5000
})
//返回的是一个promise
return instance(config)
}
用的话
import request from 'request.js'
//传入的是config,上面那个表,
request({
url: '/user/user/follow'
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
5)拦截器
分为请求拦截器和响应拦截器,写再axios得到实例封装里面的,就是返回 return instance(config)
之前
//请求拦截器
//use后第一个参数函数是成功的函数
instance.interceptors.request.use(config => {
//请求可以在这里设置一个转圈圈的东西,等在相应后,将转圈圈样式改变就可
//可以检查config里面有没有token令牌之类的
//或者检查config里面有没有不对的信息
// 都通过后再放行,如果不通过就跳转到其他页面
return config
//失败的函数
}, err => {
//code
})
//响应拦截器
instance.interceptors.response.use(res => {
//code
return res
}, err => {
//code
return err
})
51 、css常量
//:root是定义常量的
:root{
--color-text: #666;
}
//这就是使用
.xxx{
color: var(--color-text);
}
52、tabbar的编写(怎么让一个东西固定)
(0)怎么固定
先说一下怎么让一个div在页面上固定,给css
//比如固定住上面的导航条
.x{
position: fixed;
top: 0;
left: 0;
right: 0;
}
//比如在一个中心固定住
.leftSideBackground {
position: fixed;
bottom: 37px;
left: 20px;
//这样就固定了距离左下多少
width: 7px;
height: 40px;
}
(1)tabbar
只是提供一个思路,用插槽完成
因为下面的tabbat不知道会有几个,所以可以先定义一个tabbar,然后一个插槽完成,插槽里面再细分到每个item,然后用总的 MainTabBar完成。
<!-- tabbar -->
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name: "TabBar"
}
</script>
<style scoped>
#tab-bar {
/* 本身的样式 */
background-color: #f6f6f6;
height: 49px;
border-top: 1px solid #eee;
box-shadow: 0px -1px 1px rgba(150,150,150,.08);
/* 定位相关 */
position: fixed;
left: 0;
right: 0;
bottom: 0;
/* 利用flex进行布局 */
display: flex;
text-align: center;
}
</style>
<!-- tabbarItem,每一个插槽都要有活跃图片,未选择图片,文字 -->
<template>
<div id="tab-bar-item" @click="itemClick">
//记得要再slot外再用一层div包住,因为div才能用样式之类的
<div class="item-icon" v-show="!isActive"><slot name="icon"></slot></div>
<div class="item-active-icon" v-show="isActive"><slot name="active-icon"></slot></div>
<div class="item-text" :style="activeStyle"><slot name="text"></slot></div>
</div>
</template>
<script>
export default {
name: "TabBarItem",
props: {
link: {
type: String,
required: true
}
},
computed: {
isActive() {
return this.$route.path.indexOf(this.link) !== -1
},
activeStyle() {
return this.isActive ? {'color': 'red'} : {}
}
},
methods: {
itemClick() {
this.$router.replace(this.link)
}
}
}
</script>
<style scoped>
#tab-bar-item {
/* 这个很重要 */
flex: 1;
}
.item-icon img, .item-active-icon img {
width: 24px;
height: 24px;
margin-top: 5px;
vertical-align: middle;
}
.item-text {
font-size: 12px;
margin-top: 3px;
color: #333;
}
</style>
MainTabBar,往tabbar中插入一些tabbaritem
<template>
<tab-bar>
<tab-bar-item link="/home">
<img slot="icon" src="~assets/img/tabbar/home.svg" alt="">
<img slot="active-icon" src="~assets/img/tabbar/home_active.svg" alt="">
<div slot="text">首页</div>
</tab-bar-item>
<tab-bar-item link="/category">
<img slot="icon" src="~assets/img/tabbar/category.svg" alt="">
<img slot="active-icon" src="~assets/img/tabbar/category_active.svg" alt="">
<div slot="text">分类</div>
</tab-bar-item>
<tab-bar-item link="/cart">
<img slot="icon" src="~assets/img/tabbar/cart.svg" alt="">
<img slot="active-icon" src="~assets/img/tabbar/cart_active.svg" alt="">
<div slot="text">购物车</div>
</tab-bar-item>
<tab-bar-item link="/profile">
<img slot="icon" src="~assets/img/tabbar/profile.svg" alt="">
<img slot="active-icon" src="~assets/img/tabbar/profile_active.svg" alt="">
<div slot="text">我的</div>
</tab-bar-item>
</tab-bar>
</template>
<script>
import TabBar from 'common/tabbar/TabBar'
import TabBarItem from 'common/tabbar/TabBarItem'
export default {
name: "MainTabBar",
components: {
TabBar, TabBarItem
}
}
</script>
<style scoped>
</style>
52.5、flex: 1;
css中的一个属性,用于水平布局
53、前端mvc
这个问题也是困扰了我很久,就是我的请求都直接写在每个组件方法里面,这样不好
其实可以参考spring mvc,每个组件的方法请求相当于controller,然后弄个中间层,将逻辑代码放到servlet层,因为只有读取到的数据后操作不一样,读取数据的操作都是一样的。可能A组件要根据ID找数据,B组件也是根据ID找数据,那么就可以让根据ID找数据丢在一个文件里面就行
//封装好一个request.js
//传入config,config会自动覆盖的
export function request(config){
//有个axios.create专门用于创建实例
const instance = axios.create({
baseURL:'http://192.168.12.128:9012',
timeout:5000
})
//拦截器
//返回的是一个promise
return instance(config)
}
//home.js,专门处理home的东西
import requestfrom './request'
export function getHomeMultidata() {
return axios({
url: '/home/multidata'
})
}
// 别的地方要用的话
import {getHomeMultidata} from "network/home";
//返回的就是一个promis,可以then和catch
getHomeMultidata().then(res => {
//xxx
})
53、process.env.NODE_ENV
可以获得当前是什么环境,怎么拿到我就不知道了。。就只会用
54、手机浏览器看控制台
一个插件,叫vconsole
55、前端处理跨域+转发跳转
事情的原因是,发送一个http://localhost:8080/applySiCard/applyInfo/v1/realNameInfo的请求,等半天没反应,就觉得很奇怪,大佬说这里发送的是localhost,但实际上vue进行了设置
//vue.config.js
module.exports = {
devServer: { // 包含关系
proxy: {
'/applySiCard': {
target: 'http://10.11x.x.x:8099',
// target: 'http://192.168.xxx:8099',
changeOrigin: true //跨域
}
}
}
},
这个做了转发也做了跨域问题,只要是localhost:8080/applySiCard/xxx
的,都会跳转到10.11x.x.x:8099/applySiCard/xxx
,F12也看不到的
这里是做了一个转发的,这样就不用在每个请求都加ip和端口了
值得一提的是,这些操作devServer.proxy
都是用于开发环境的,开发环境才会有跨域问题,为了解决跨域才会有这个操作。因为本地是127.0.0.1,会有跨域。
如果是线上,前端和后端在同一个主机上,地址当然耶一样,不用再改了。
56、setInterval和clearInterval
settimeout是多少秒仅执行一次后消失了嘛,这玩意是隔多少秒就执行一次
clearInterval是用来取消的
比如发验证码,60s内不允许重复发
startBusRefresh() {
if (this.busRefreshTimer) return
this.time = 60
this.busRefreshTimer = setInterval(() => {
this.time--
if (this.time == 0) {
this.stopBusRefresh()
}
}, 1 * 1000)
},
stopBusRefresh() {
this.busRefreshTimer && clearInterval(this.busRefreshTimer)
this.busRefreshTimer = null
},
57、vue中,方法异步用async关键字
async examin() {
//xxx
}
58、this.$forceUpdate()强制刷新
原理是调用强制更新方法this.$forceUpdate()会更新视图和数据,触发updated生命周期
能少用就少用吧,刷新一下对用户体验不友好
59、!important 解决兼容性
.el-table__expanded-cell:hover {
background-color: #f7f7f7 !important;
}
60、/deep/、::v-deep、>>>
这3都是表示一个东西,修改原生样式
这得从一件诡异的事情说清
公司做一个弹窗嘛
二话不说上vant的官网复制粘贴去
然而复制粘贴后,却啥都没有
①为什么会这样
?!为啥这样,对比了一下官方的css,发现样式被覆盖了
这背景都被搞了,是被自己写的全局css覆盖了。。。
所以我们现在在vue文件里面改局部css样式就好(不要搞全局css)
这是官网的:
尽管有点不一样,但不要怕,把背景搞成白色就算赢
不知道为啥上面是有#app .van-action-sheet
,而官方是.van-action-sheet
,可能是全局变量设置了什么东西吧。。。
然后就改
②错误1
.van-action-sheet{
background: white;
}
真是非常神奇呢,然后看了网上说是改原生样式,会在后面加一串东西
③错误2
/deep/ .van-action-sheet{
background: white;
}
嗯?不会。。
④正确
简单且朴素
#app{
.van-action-sheet{
background: white;
}
}
我也不知道为什么后面有那串东西还能成功
61、根据什么环境读取不同的参数数据
.env 全局默认配置文件,不论什么环境都会加载合并
.env.development 开发环境下的配置文件
.env.production 生产环境下的配置文件
PS:属性名必须以VUE_APP_开头,比如VUE_APP_XXX
如
# .env.development
VUE_APP_MOCK_LOCAL = true
用的话:
const isMockLocal = process.env.VUE_APP_MOCK_LOCAL;
62、win和linux的node环境不一样
忘记哪个依赖了,install后会报错说没有安装python,然后安装个python就行(有些Linux自带py环境)。然后还有一个是淘宝镜像的问题,在后面加--registry=https://registry.npm.taobao.org
63、路由请求时转圈圈
这种,可以在一个请求方法里面写,也可以搞一个通用post里面写,我这在一个请求方法里面写的
export function queryARES(param, showLoading = true) {
let loading = null
//等待状态
if (showLoading) {
loading = Toast.loading({
duration: 40 * 1000, // 持续展示 toast
forbidClick: true,
message: '正在处理,请稍等'
})
}
return new Promise((resolve) => {
return post({
param: param,
url: '/openapi/v1/mohrss/call_api',
}).then((res) => {
//清除等待状态
loading?.clear()
resolve(res);
}).catch((error) => {
//清除等待状态
loading?.clear()
console.log(error);
resolve(false);
});
});
}
64、async的作用
我以前一直以为async是为了解决异步问题,现在发现是配合promise使用的
如果一个请求返回promise,没有async为
created() {
const res = xxx(param, true)
console.log(res)
},
如果有async
async created() {
const res = await xxx(param, true)
65、富文本编辑器
npm install @packy-tang/vue-tinymce
把https://gitee.com/panjiachen/vue-element-admin/tree/master/src/components中的Tinymce复制下来到一样的地方(对,npm安装后还不能直接用。。而且这个是已经编写好的了,很方便)
然后用就简单了
<template>
<div class="components-container">
<aside>
这是富文本编辑器,用v-model绑定html形式的值
</aside>
<div>
<tinymce v-model="content" :height="300" />
</div>
<aside>
这是富文本编辑器的结果,用v-html绑定html形式的值。保存到数据库直接用html形式保存即可
</aside>
<div class="editor-content" v-html="content" />
</div>
</template>
<script>
import Tinymce from '@/components/Tinymce'
export default {
name: 'TinymceDemo',
components: { Tinymce },
data() {
return {
content:
`<h1 style="text-align: center;">Welcome to the TinyMCE demo!</h1><p style="text-align: center; font-size: 15px;"><ul>
<li>Our <a href="//www.tinymce.com/docs/">documentation</a> is a great resource for learning how to configure TinyMCE.</li><li>Have a specific question? Visit the <a href="https://community.tinymce.com/forum/">Community Forum</a>.</li><li>We also offer enterprise grade support as part of <a href="https://tinymce.com/pricing">TinyMCE premium subscriptions</a>.</li>
</ul>`
}
}
}
</script>
<style scoped>
.editor-content{
margin-top: 20px;
}
</style>
66、图片加载不了
普通的<img src='https://dgapa.dongguantong.com.cn/dgtmgm/upload/image/20210801115039.jpg' alt="" style="height: 50px;width: 50px">
就加载不出来呢
然后百度说加这个就好了 <meta name="referrer" content="no-referrer" />
67、非父子组件传值
vuex
第一个方法就是vuex了,在前面已经介绍
新建一个Vue对象busEvent
新建一个Vue对象作为中间值
// 创建busEvent.js 作用:非父子组件间传值
import Vue from 'vue'
const busEvent = new Vue()
busEvent.dicts = []
export default busEvent
然后用的话
import busEvent from '@/utils/busEvent'
//xxxx函数里面,读取缓存
if (busEvent.dicts[dict_type]) {
this.dicts[dict_type] = busEvent.dicts[dict_type]
return busEvent.dicts[dict_type]
}
//写缓存
busEvent.dicts[dict_type] = res.data
return busEvent.dicts[dict_type]
68、注册全局的$xxx
之前不是会有$router
表示路由嘛,我们也想要$xx
自己的也可以的
//main.js
import Vue from 'vue'
import * as echarts from 'echarts';
Vue.prototype.$echarts = echarts
69、父组件用子组件的方法
<template slot="content">
<!-- 要加个ref就对了-->
<userinfo-Mgt-Table ref="userinfoMgtTable" />
</template>
<script>
import userinfoMgtTable from "@/components/Table/personas/userinfoMgtTable";
export default {
components: {
userinfoMgtTable,
},
}
</script>
然后子组件userinfoMgtTable里面有个方法searchByParam
//userinfoMgtTable
searchByParam(param) {
this.pageNum = 1;
this.searchParam = Object.assign({}, param);
this.reqBillingRecordList();
},
父组件要用的话
this.$refs.子组件名.方法
this.$refs.userinfoMgtTable.searchByParam(param);
70、Object.assign(a,b)
这可是个好东西,让b的属性加到a里面,
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
Object { a: 1, b: 4, c: 5 }
71、Object.keys(object).forEach(e => {您的代码}) 用于查看对象的key
类似于for…i…
下面两段代码的结果都一样
// 创建对象
var person = {
name: 'hjm',
age: 18,
school:"SKD University",
home: 'China'
}
// 用forEach()进行遍历
var keys = Object.keys(person).forEach(function (e) {
console.log("person ", e, ": ", person[e]);
});
// 创建一个对象
var person = {
name: 'hjm',
age: 18,
school:"SKD University",
home: 'China'
}
// 获得对象属性名组成的数组
var keys = Object.keys(person);
// 用于存储匹配的属性值
var value = [];
// 用for进行遍历
for (var i = 0,len = keys.length; i < len; i++) {
var key = keys[i];
value[i] = person[key];
console.log("person ", key, ": ", value[i]);
}
用于将对象下标对应的值为空
searchForm = {
user_id: "asd",
age_tag: "qwe",
sex_tag: "r",
card_type_tag: "w",
active_tag: "q",
parking_tag: "s",
},
//将对象值为空
Object.keys(this.searchForm).forEach(key => {
this.searchForm[key] = ''
console.log(this.searchForm)
})
sex_tag
age_tag
card_type_tag
active_tag
parking_tag
ex_tag
72、开发依赖和生产依赖
就开发的时候嘛,有些是要用的,但是正式上线就不用了,就npm install xxx --save-dev
普通的就npm install xxx --save
73、mock的简单使用
以前的mock我都是参照vue element admin他里面做好的来做的,但是我直接复制过去发现不行,就参考了这个文章是可以的
PS:最好的办法还是vue element admin里面的mock方法,但是我不会抽出来,只能用比较差一点的方法
npm install mockjs --save-dev
1、在src同级目录添加mock-server文件夹内涵3个文件如下
index.js //假接口入口配置文件
similar.js //小模块,不让一整个模块都写在index里面
2、vue.config.js内配置
devServer:{
proxy: config.dev.proxyTable,//参照位置
...
before: require('./mock-server/index'), //添加
}
3、index.js
//导入拆分的模块
const similar = require('./similar')
module.exports = function (app) {
//然而我写得还是不好,还是要一个个地导入
similar.querySimilarUserPersonaListByPage(app);
similar.querySimilarUserPersonaNum(app)
}
similar.js
const Mock = require('mockjs');
const Host = process.env.VUE_APP_HOST + '/persona/label/v1'
//生成mock地随机数
const json = Mock.mock({
result_code: '@integer(1, 3)',
message: "success",
data: {
id: '@increment',
title: '@title(5, 10)',
}
})
module.exports = {
//里面就写各个的方法
querySimilarUserPersonaListByPage(app) {
app.post(Host + '/querySimilarUserPersonaListByPage', function (rep, res) {
res.json(Mock.mock(json));
});
},
querySimilarUserPersonaNum(app) {
app.post(Host + '/querySimilarUserPersonaNum', function (rep, res) {
res.json(Mock.mock(json));
});
}
}
73.5、由于mockjs很久没人理了,所以推荐使用Better-Mock
Better-Mock基于mockjs,所以可以兼容使用
Better-Mock文档
mock文档
主要是Better-Mock可以直接拦截
74、数据字典的几种使用方法
过滤器
//main.js
import * as filters from './filters'
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
// src/filter/index.js
// 导入的2个就是json
import {trans_tp, payment_type} from '@/views/scenesPay/dict'
export function formatScenesDict(value) {
let flag = false
for (let transTpElement of trans_tp) {
if (value === transTpElement.code) {
flag = true
return transTpElement.name
}
}
for (let paymentTypeElement of payment_type) {
if (value === paymentTypeElement.code) {
flag = true
return paymentTypeElement.name
}
}
if (flag == false) return value
}
// src/views/scenesPay/dict
export const payment_type = [
{code: '0101', name: "中银通支付"},
{code: '0102', name: "微众虚拟卡支付"},
{code: '0103', name: "东莞银行虚拟卡支付"},
{code: '0104', name: "广发银行虚拟卡支付"},
{code: '0105', name: "农商银行虚拟卡支付"},
{code: '0201', name: "联盟银行信用卡支付"},
{code: '0202', name: "非联盟信用卡支付"},
{code: '0203', name: "联盟银行储蓄卡支付"},
{code: '0204', name: "非联盟银行储蓄卡支付"},
{code: '0205', name: "社保卡支付"},
{code: '0206', name: "储蓄卡快捷支付"},
{code: '0207', name: "信用卡快捷支付"},
{code: '0301', name: "微信支付"},
{code: '0302', name: "支付宝支付"},
{code: '0303', name: "云闪付支付"},
]
75、判断当前是什么环境
环境只有3个,开发、测试、生产,其中,开发和测试是可以直接判断的
process.env.NODE_ENV 为development时,开发环境
为production时,是生产环境
判断是否为测试环境
这个大有研究,从原理说起
//package.json
{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test-build": "vue-cli-service build --mode testpdct",
"lint": "vue-cli-service lint"
},
}
一般比如看这里嘛,第一个是开发,第二个是生产
第三个是我们自定义的,自定义为测试,后面的–mode testpdct就是让我们用了.env.testpdct这个文件
一般来说,配置什么全局变量的都是这样的
开发和生产是用vue cli创建就有的,testpdct是人工创建的,名字就是package.json里面的--mode xxxx
然后在.env.testpdct里面定义一个VUE_APP_ENV = test
,用的时候process.env.VUE_APP_ENV === 'test'
就能判断是不是测试环境了
76、更新依赖
直接npm update xxxx
77、组件传值不单单是传值
比如这个
<van-empty
:image="image"
v-if="bus_tickets&&bus_tickets.length == 0">
<template slot="description">
<span>您还没有行程 赶紧下单吧! <br> <div class="clickTips" @click="refresh">点我刷新试试</div></span>
</template>
</van-empty>
<style>
.van-empty {
padding: 0;
/deep/ .van-empty__image {
width: 135px;
height: 85px;
margin-top: 57px;
margin-bottom: 15px;
}
/deep/.van-empty__description {
margin-top: 0;
.clickTips {
text-align: center;
color: rgb(94, 201, 255);
}
}
}
</style>
就,原本description只传个文字,但是用<template slot="description">
后,里面可以添加样式啥的,,而且那cssvan-empty__description
还能直接用??
78、传图片路径失败时还可以用require来做
<van-empty :image="image">
</van-empty>
<script>
data () {
return {
image: require('@/assets/images/empty.png')
}
},
</script>
79、$on和$emit
进行非父子组件通讯
非父子组件通讯
//busEvent.js
// 创建busEvent.js 作用:非父子组件间传值
import Vue from 'vue'
const busEvent = new Vue()
export default busEvent
//一般来说这么用的
//用之前记得import busEvent
var bus = new Vue()
// 在组件 B 绑定自定义事件
bus.$on('id-selected', function (id) {
// ...
})
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
80、阻止冒泡事件
每个按钮都有@click
事件,然后按钮里面有个X也有@click
事件,但是按了X,连外面button得事件也触发了,为了解决,这就是阻止冒泡事件(大学干小南教过)
vue解决就是在X得事件加stop@click.stop="xxx"
https://cn.vuejs.org/v2/api/#v-on
81、保存登录状态
一般来说,登录后,保存到localStorage和vuex,这样,不刷新不退出的情况下就能一只知道登录状态和用户了
但是一般都会刷新的,那么就用到全局路由前置守卫
router.beforeEach((to, from, next) => {
}
在每次要跳转之前,判断localStorage的用户信息是否存在,如果不存在,就没登录。如果存在,就把localStorage中的用户信息同步到vuex进行相应化数据,其他用就getter用就好了
比如
router.beforeEach((to, from, next) => {
const uniUserInfo = uni.getStorageSync('userInfo')
const uniToken = uni.getStorageSync('token')
if (uniToken){
if (uniUserInfo){
store.dispatch('user/setToken', uniToken)
store.dispatch('user/setUserInfo', uniUserInfo)
}else{
setTokenAndUserInfo(uniToken)
}
}
// console.log(uniToken)
// console.log(uniUserInfo)
next();
});
82、el-select中的el-option中value为对象时
比如这个
<el-form-item label="上一级字典" prop="sup_dict">
<el-select v-model="sup_dict" placeholder="请选择" >
<el-option v-for="item in dictInfo" :label="item.dict_name" :value="item" :key="item.id"></el-option>
</el-select>
</el-form-item>
//dictInfo为
dictInfo:[
{
"id": "d3f34d529e26494eb974772ccc8d4612",
"dict_no": "1",
"dict_name": "阵地打卡活动",
"dict_type": "activity_type",
"dict_desc": null,
"sup_dict_no": null,
"sup_dict_type": null,
"field1": "10",
"field2": null,
"field3": null,
"status": "1",
"create_time": "2021-11-11 10:11:08",
"update_time": null,
"is_delete": "0"
},
{
"id": "e152ef7a0f374a8389b2d5219f6921fd",
"dict_no": "2",
"dict_name": "主题积分活动",
"dict_type": "activity_type",
"dict_desc": null,
"sup_dict_no": null,
"sup_dict_type": null,
"field1": "100",
"field2": null,
"field3": null,
"status": "1",
"create_time": "2021-11-11 10:11:49",
"update_time": null,
"is_delete": "0"
},
{
"id": "181c11e860fa44e79b1c12dedf6c31ae",
"dict_no": "1",
"dict_name": "实体卡刷卡",
"dict_type": "sign_type",
"dict_desc": null,
"sup_dict_no": null,
"sup_dict_type": null,
"field1": "呵呵",
"field2": null,
"field3": null,
"status": "1",
"create_time": "2021-11-10 15:28:46",
"update_time": "2021-11-11 09:03:43",
"is_delete": "0"
},
{
"id": "78fef122fdbb416a99f3d951b3254cfa",
"dict_no": "2",
"dict_name": "手动补卡",
"dict_type": "sign_type",
"dict_desc": null,
"sup_dict_no": null,
"sup_dict_type": null,
"field1": "无",
"field2": null,
"field3": null,
"status": "1",
"create_time": "2021-11-11 10:09:15",
"update_time": null,
"is_delete": "0"
},
{
"id": "909176513a6240cd889c13008e803ed7",
"dict_no": "3",
"dict_name": "人脸打卡",
"dict_type": "sign_type",
"dict_desc": null,
"sup_dict_no": null,
"sup_dict_type": null,
"field1": "无",
"field2": null,
"field3": null,
"status": "1",
"create_time": "2021-11-11 10:09:45",
"update_time": null,
"is_delete": "0"
},
{
"id": "f3e65a8b463d4f69b51700eb0c0f77b9",
"dict_no": "1",
"dict_name": "正常",
"dict_type": "status",
"dict_desc": null,
"sup_dict_no": null,
"sup_dict_type": null,
"field1": "无",
"field2": null,
"field3": null,
"status": "1",
"create_time": "2021-11-11 10:13:00",
"update_time": null,
"is_delete": "0"
},
{
"id": "85471e06f1024bfe9dbca5c4622f6b55",
"dict_no": "2",
"dict_name": "失效(置黑)",
"dict_type": "status",
"dict_desc": null,
"sup_dict_no": null,
"sup_dict_type": null,
"field1": "无",
"field2": null,
"field3": null,
"status": "1",
"create_time": "2021-11-11 10:13:24",
"update_time": null,
"is_delete": "0"
}
]
就很简单的,选项为for循环,显示为dict_name,点中后值为整个item对象,通过v-model绑定到sup_dict中,但是,事实是这样
为什么都乱了。。。
查文档
就是说要在el-select加多个value-key=“id”,其中id为唯一
<el-form-item label="上一级字典" prop="sup_dict">
<el-select v-model="sup_dict" placeholder="请选择" value-key="id">
<el-option v-for="item in dictInfo" :label="item.dict_name" :value="item" :key="item.id"></el-option>
</el-select>
</el-form-item>
就好了
83、连接日期和时间,返回Date
const date = '2018-12-24';
const time = '23:59:59';
const dateTime = moment(`${date} ${time}`, 'YYYY-MM-DD HH:mm:ss').format();
84、解决异步的邪门歪道,诡异的办法
有些东西就尼玛离谱,逻辑上一点毛病都没有,但他妈的vue或者是js就是存在着这些异步同步的问题。
比如
console.log(this.form)
this.form = obj2DateRange(this.form)
console.log(this.form)
明明经过函数的变化都已经发生改变了,但2次输出都是变化后的效果,但老子要的是一个变化前和一个变化后的效果啊
再比如提交表单的
const reqParam = this.form
//然后将reqParam提交上去,进行更新
但这个,修改里面变的情况下,外面你的table也会变,但这还没提交呢
遇事不决,遇到这种人眼逻辑看起来没毛病的,我们可以统一用一种诡异的方法解决
this.form.id = row.id
this.form.act_no = row.act_no
this.form.act_name = row.act_name
this.form.act_type_no = row.act_type_no
this.form.site_no = row.site_no
this.form.act_desc = row.act_desc
this.form.status = row.status
this.form.act_duration = row.act_duration
this.form.score_once = row.score_once
this.form.dateRange = row.dateRange
this.form.start_date = row.start_date
this.form.start_time = row.start_time
this.form.end_date = row.end_date
this.form.end_time = row.end_time
//this.form = row //感觉和这一样,但效果确实不一样
或者
const {
id,
act_no,
act_name,
act_type_no,
site_no,
act_desc,
status,
act_duration,
score_once,
dateRange, //在做了在做了
start_date,
start_time,
end_date,
end_time
} = this.form
const reqParam = {
id,
act_no,
act_name,
act_type_no,
site_no,
act_desc,
status,
act_duration,
score_once,
dateRange, //在做了在做了
start_date,
start_time,
end_date,
end_time
}
//const treqParam = this.form //看起来和这一样,但实际上就尼玛不一样
看起来就是搁这搁这得感觉,但实际上他就是能解决一些异步得奇怪行为,就尼玛 诡异离谱