vue2高级用法

vue高级用法

1.混入mixin

(共用,每个组件引入独立存在)

//本质:扩展组件的对象与方法,可以共用
优先级: 值为对象,methods,components中,若发生冲突,取组件
        值为函数,如created,mounted,混入先于组件调用
用法:
1.src/mixins文件夹/myMixins.js
   export const myMixin= {
 data() {
  return {
   name: 'mixin'
  }
 },
 created() {
  console.log('mixin...', this.name);
 },
 mounted() {},
 methods: {}
}
2.在vue文件中使用
import {myMixin}  '@/mixins/myMixins.js'; // 引入mixin文件
export default {
 mixins: [myMixin]
}

2.父子组件传值

第一种:
父组件v-bind绑定属性
<myComponent :title="data"></myComponent>
子组件通过props接收绑定属性,可以通过数组或者对象形式接收
props:{
title:{
    type: String,                 //可指定接收类型,如:Array.
     default:"this is default"     //可设置默认值
}
}

第二种:
通过provide inject   无论层级多深都可以传递  数据不是响应式的
父组件provide 
provide(){
return {
yeye:this
}
}
子组件inject引入
inject:['yeye']

通过watch中监听获取到的方法,实现响应式数据
父组件:
<template>
    <div class="parent-container">
      Parent组件
      <br/>
      <button type="button" @click="changeName">改变name</button>
      <br/>
      Parent组件中 name的值: {{name}}
      <Child  v-bind="{name: 'k3vvvv'}" />
    </div>
</template>
<script>
import Child from './Child'
export default {
  name: 'Parent',
  data () {
    return {
      name: 'Kevin'
    }
  },
  methods: {
    changeName (val) {
      this.name = 'New Kevin'
    }
  },
  provide () {
    return {
      nameFromParent: this.name,
      getReaciveNameFromParent: () => this.name //将一个函数赋值给provide的一个值,这个函数返回父组件的动态数据
    }
  },
  components: {
    Child
  }
}
</script>


子组件:
<template>
  <div class="child-container">
    Child组件
    <br/>
    <GrandSon />
  </div>
</template>
<script>
import GrandSon from './GrandSon'
export default {
  components: {
    GrandSon
  }
}
</script><template>
  <div class="child-container">
    Child组件
    <br/>
    <GrandSon />
  </div>
</template>
<script>
import GrandSon from './GrandSon'
export default {
  components: {
    GrandSon
  }
}
</script>


孙组件:
<template>
  <div class="grandson-container">
    Grandson组件
    <br/>
    {{nameFromParent}}
    <br/>
    {{reactiveNameFromParent}}
  </div>
</template>
<script>
export default {
  inject: ['nameFromParent', 'getReaciveNameFromParent'],
  computed: {
    reactiveNameFromParent () {
      return this.getReaciveNameFromParent()//子孙组件里面调用这个函数
    }
  },
  watch: {
    'reactiveNameFromParent': function (val) {
      console.log('来自Parent组件的name值发生了变化', val)//监听数据变化
    }
  },
  mounted () {
    console.log(this.nameFromParent, 'nameFromParent')
  }
}
</script>


第三种
$parent  可以获取父组件数据和方法
$children   可以获取子组件数据和方法

3.子组件触发父组件事件

第一种:父组件调用方法
父组件定义一个事件
示例:<menu-item @show="showFather"></menu-item>
子组件通过$emit触发
<button @click="$emit('show',111)">触发父组件并传值</button>

第二种:父组件通过加.sync然后 data中定义属性      子组件通过update:xxx
如果是用sync写法 可以如下:
子组件
<button @click="$emit('update:show',111)">触发父组件并传</button>
<!--
下面为简写
<menu-item @update:show="newVal=>showFather=newVal"></menu-item>
-->
<menu-item :show.sync="showFather"></menu-item>
showFather在data中声明

4.eventBus事件总线

//1.先定义一个事件中心
var eventBus=new Vue()

//监听与销毁
eventBus.$on('add',(getData)=>{//靠回调接收数据(j)
    console.log(`接收得数据为:${getData}`)
})
evnetBus.$off('add')

//触发
eventBus.$emit('add',参数) //传递数据方

5.vue组件绑定原生事件

//组件注册事件时添加.native,事件逻辑写在methods中即可
比如:
<child @click.native="handleClick"></child>

6.插槽

<!--1.匿名插槽(默认插槽)-->
父组件
<template>
  <div>
    <h3>这是父组件</h3>
    <son>实践slot</son>  //此处的实践slot会在子组件中slot标签展示
  </div>
</template>
-------------------------------------------------------------
子组件
<template>
  <div>
    <h4>这是子组件</h4>
    <input type="text" placeholder="请输入">
    <slot></slot>   //展示实践slot
  </div>
</template>
<!--2.具名插槽-->
父组件
<template>
  <div>
    <h3>这是父组件</h3>
    <son>
       <template slot="myslot">
          <div>实践具名slot</div>
       </template>
    </son>
  </div> 
</template>
----------------------------------------------------
子组件
<template>
  <div>
    <h4>这是子组件</h4>
    <input type="text" placeholder="请输入">
    <slot name="myslot"></slot>
  </div>
</template>
<!--3.作用域插槽-->
父组件
<template lang="">
  <div>
    <h3>这是父组件</h3>
    <son>
      <template slot="myslot" slot-scope="scope"> 
     <!--通过scope.data拿子组件数据 ,这个data是子组件自定义属性名,需与子组件保持一致-->
        <ul>
          <li v-for="item in scope.data">{{item}}</li>
        </ul>
      </template>
    </son>
  </div> 
</template>
---------------------------------------------------------
子组件
<template>
  <div>
    <h4>这是子组件</h4>
    <slot name="myslot" :data='list'></slot>  <!--属性名为data-->
  </div>
</template>
 
<script>
  export default {
    name:'Son',
    data(){
      return{
        list:[
          {name:"Tom",age:15},
          {name:"Jim",age:25},
          {name:"Tony",age:13}
        ]
      }
    }
  }
</script>

7.动态组件

//通过使用保留的 <component> 元素,动态地绑定到它的 is 特性,可以实现动态组件
<component :is="组件名称"></component>
//示例:
<template>
  <div class="info">
    <component :is="roleComponent" v-if="roleComponent" />
  </div>
</template>
<script>
    //展示的组件
import AdminInfo from './admin-info'
import BookkeeperInfo from './bookkeeper-info'
import HrInfo from './hr-info'
import UserInfo from './user-info'
export default {
  components: {
    AdminInfo,
    BookkeeperInfo,
    HrInfo,
    UserInfo
  },
  data() {
    return {
        //根据不同角色展示不同的组件
      roleComponents: {
        admin: AdminInfo,
        bookkeeper: BookkeeperInfo,
        hr: HrInfo,
        user: UserInfo
      },
      role: 'user',
      roleComponent: undefined
    }
  },
  created() {
    const { role, roleComponents } = this
    this.roleComponent = roleComponents[role]
  }
}
</script>

8.组件缓存

//注意:要求同时只有一个子元素被渲染。若其中有v-for则无效
常用方式
方案一 动态组件  内置组件<component></component>
<keep-alive>
  <component :is="view"></component>
</keep-alive>
方案二:当出现条件判断
<keep-alive>
  <comp-a v-if="a > 1"></comp-a>
  <comp-b v-else></comp-b>
</keep-alive>
//方案三  结合路由使用
<keep-alive>
     <router-view></router-view>
 </keep-alive>
属性:
include        只有名称包含才缓存
exclude         名称包含不缓存
示例:可以使用正则,分隔字符串,数组
<keep-alive include="a,b"></keep-alive>
<keep-alive :include="/a|b/"></keep-alive>
<keep-alive :include="['a', 'b']"></keep-alive>

9.watch监听

常规用法

data:{
    a:1,
    b:{
        c:1
  },
      dataForm:{
          validType:0
      }
},
watch:{
    a(val,oldVal){   //普通监听
          console.log('a'+val,oldVal)
    },
    b:{//深度监听,可监听到对象、数组的变化
        handler(val,oldVal){
             console.log('b.c:'+val.c,old.c)
        },
        deep:true,  //深度监听
       immediate: true  //立即监听(默认false)
    },
        //监听对象中某个
    'dataForm.validType':{
      handler(val){
        if(val===3){
            //to do
        }
      }
    },
}

高阶使用

1.触发监听执行多个方法
使用数组可以设置多项,形式包括字符串、函数、对象
export default {
    data: {
        name: 'Joe'
    },
    watch: {
        name: [
            'sayName1',
            function(newVal, oldVal) {
                this.sayName2()
            },
            {
                handler: 'sayName3',
                immaediate: true
            }
        ]
    },
    methods: {
        sayName1() {
            console.log('sayName1==>', this.name)
        },
        sayName2() {
            console.log('sayName2==>', this.name)
        },
        sayName3() {
            console.log('sayName3==>', this.name)
        }
    }
}

2.watch监听多个变量
//watch本身无法监听多个变量。但我们可以将需要监听的多个变量通过计算属性返回对象,再监听这个对象来实现“监听多个变量”

export default {
    data() {
        return {
            msg1: 'apple',
            msg2: 'banana'
        }
    },
    compouted: {
        msgObj() {
            const { msg1, msg2 } = this
            return {
                msg1,
                msg2
            }
        }
    },
    watch: {
        msgObj: {
            handler(newVal, oldVal) {
                if (newVal.msg1 != oldVal.msg1) {
                    console.log('msg1 is change')
                }
                if (newVal.msg2 != oldVal.msg2) {
                    console.log('msg2 is change')
                }
            },
            deep: true
        }
    }
}

10.vue中配置开发环境、生产环境、测试环境

//1、根目录下新建文件:.env.development(开发环境)、.env.test(测试环境)、.env.production文件(生产环境)
.env    //全局默认配置文件,无论什么环境都会加载合并。(优先级<环境配置文件)

.env.development(开发环境)配置内容
NODE_ENV = 'development'  //模式
VUE_APP_MODE = 'development'  //通过"VUE_APP_MODE"变量来区分环境
VUE_APP_BASE_URL = 'http://192.****:8008/' //api地址

.env.test(测试环境)配置内容
NODE_ENV = 'test'
VUE_APP_MODE = 'test'
VUE_APP_BASE_URL = 'http://xxx.xxx.xxx.xx:8008/'
//打包输出目录
outputDir = dist-test

.env.production文件(生产环境)配置内容
NODE_ENV = 'production'
VUE_APP_MODE = 'production'
VUE_APP_BASE_URL = 'http://xxx.xxx.xxx.xx:8008/'
outputDir = dist-production

//2.修改vue.config.js
console.log(process.env.VUE_APP_BASE_URL)//打印环境
module.exports = {
    // 基本路径,相对路径
    publicPath: "./",
    // 输出文件目录
    outputDir: process.env.outputDir,
    productionSourceMap:process.env.NODE_ENV==='production'?false:true,//生产环境下设置source map为false加速构建
    devServer:{
      proxy:{
         '/api':{
           target:process.env.VUE_APP_BASE_URL,//使用的时候调用process.env
           changeOrigin:true,//开启跨域(修改y
           ws:false,//websocket不支持
           pathRewrite:{
               '^/api':''          
           }
         }
      }
    }
}

//3.配置package.json     (执行对应的npm run xxx)
"scripts"{
    //打包开发环境
    "serve":"vue-cli-service build --mode development",
    //打包生产环境    
    "build":"vue-cli-service build --mode production",
    //打包测试环境
    "test": "vue-cli-service build --mode test", 
    "lint":"vue-cli-service lint"
}
  ----------------------------------------- 
   // 4.判断并使用不用的开发环境配置
if(process.env.VUE_APP_MODE==='development'){
    //开发环境下的执行操作
}else if(process.env.VUE_APP_MODE==='test'){
    //测试环境下的执行操作
}else{
    //生产环境下的执行操作
}
自定义环境变量必须以VUE_APP_开头
NODE_ENV 
BASE_URL 
//只有三种才能生效

11.观察组件中的任何内容

export default {
  computed: {
    someComputedProperty() {
      // 更新计算道具
    },
  },
  watch: {
    someComputedProperty() {
      // 当计算的 prop 更新时做一些事情
    }
  }
};

11.nextTick()

//this.$nextTick()作用:帮助我们在改变组件中属性后,立刻拿到渲染以后的dom节点对象
this.$nextTick(()=>{

})

12.监听组件生命周期

//使用 @hook 即可监听组件生命周期,组件内无需做任何改变。同样的, created 、 updated 等也可以使用此方法

<template>
    <List @hook:mounted="listenMounted" />
</template>
<script>
    export default{
         methods:{
              listenMounted(){

            }
        }
    }
</script>

13.定时器优雅清除方式

//我们可以通过 $on 或 $once 监听页面生命周期销毁来解决这个问题:
export default {
    mounted() {
        this.creatInterval('hello')
        this.creatInterval('world')
    },
    methods:{
        creatInterval(msg) {
        let timer = setInterval(() => {
            console.log(msg)
        }, 1000)
        this.$once('hook:beforeDestroy', function() {
            clearInterval(timer)
        })
    }
    }
}

14. v-for遍历的数据配合ElementUI表单校验

  
<template>
         ....
<el-row v-for=" (item,i) in dataForm.iconNavLayer.menuConfigs" :key="i">
      <el-col :span="12">
        <el-form-item :label="'图标'+(i+1)" 
                      :rules="rules.menuTitle" 
                      :prop="'iconNavLayer.menuConfigs.'+i+'.menuTitle'">   //遍历的数据校验
          <el-input v-model="item.menuTitle"></el-input>
        </el-form-item>
      </el-col>

      <el-col :span="12">
        <el-form-item label="排序" 
                      :rules="rules.menuSort"
                      :prop="'iconNavLayer.menuConfigs.'+i+'.menuSort'"> 
          <el-select v-model="item.menuSort" placeholder="选择排序">
            <el-option v-for="item in sortNumber" :key="item" :label="item" :value="item">
            </el-option>
          </el-select>
        </el-form-item>
      </el-col>
</el-row>
</template>
        
        
<script>
export default {
   data(){
     return {
       dataForm: {
           name: '',
           componentType: '',
           enableStartDate: '',
           remark: '',
           iconNavLayer: {
               menuConfigs: [
                   { menuTitle: '图标1', menuSort: 1, btnImgUrl: '', linkTarget: '' },
                   { menuTitle: '图标2', menuSort: 2, btnImgUrl: '', linkTarget: '' },
               ],
           moreName: '更多',
           moreIsShow: true,
           moreUrl: '',
           moreImgUrl: '',
        },
      },
       }
   }
}      
 </script>

15.vue监听手机物理返回键(浏览器返回)

//1、挂载完成后,判断浏览器是否支持popstate
mounted(){
  if (window.history && window.history.pushState) {
    history.pushState(null, null, document.URL);
    window.addEventListener('popstate', this.goBack, false);
  }
},
//页面销毁时,取消监听。否则其他vue路由页面也会被监听
destroyed(){
  window.removeEventListener('popstate', this.goBack, false);
},
//3、将监听操作写在methods里面,removeEventListener取消监听内容必须跟开启监听保持一致,所以函数拿到methods里面写
methods:{
  goBack(){
    this.$router.replace({path: '/'});
    //replace替换原路由,作用是避免回退死循环
  }
}

16. img标签动态切换图片

 <img :src=" require( active ?'../assets/bottomTabs/icon_shouye.png' :'../assets/bottomTabs/icon_shouye_n.png')" />

17.前端路由的模式

1.hash模式
原理  监听hash的变化(hashchange),渲染不同的UI 

2.history模式:
刷新会去访问该路径下的静态资源index.html,无果,报错404,
解决:
需要后端配置nginx,rewrite找不到文件时,强制指向静态资源index.html

location / {
  try_files $uri $uri/ /index.html;
}

vue-cli配置

//3x版本中需要新建vue.config.js
const path =  require('path');
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV);
const resolve = (dir) => path.join(__dirname, dir);
module.exports = {
    publicPath: process.env.NODE_ENV === 'production' ? '/site/vue-demo/' : '/',  // 公共路径
    indexPath: 'index.html' , // 相对于打包路径index.html的路径
    outputDir: process.env.outputDir || 'dist', // 'dist', 生产环境构建文件的目录
    assetsDir: 'static', // 相对于outputDir的静态资源(js、css、img、fonts)目录
    lintOnSave: false, // 是否在开发环境下通过 eslint-loader 在每次保存时 lint 代码
    runtimeCompiler: true, // 是否使用包含运行时编译器的 Vue 构建版本
    productionSourceMap: !IS_PROD, // 生产环境的 source map
    parallel: require("os").cpus().length > 1, // 是否为 Babel 或 TypeScript 使用 thread-loader。该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
    pwa: {}, // 向 PWA 插件传递选项。
    chainWebpack: config => {
        config.resolve.symlinks(true); // 修复热更新失效
        // 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
        config.plugin("html").tap(args => {
            // 修复 Lazy loading routes Error
            args[0].chunksSortMode = "none";
            return args;
        });
        config.resolve.alias // 添加别名
            .set('@', resolve('src'))
            .set('@assets', resolve('src/assets'))
            .set('@components', resolve('src/components'))
            .set('@views', resolve('src/views'))
            .set('@store', resolve('src/store'));
    },
    css: {
        extract: IS_PROD,
        requireModuleExtension: false,// 去掉文件名中的 .module
        loaderOptions: {
                // 给 less-loader 传递 Less.js 相关选项
                less: {
                    // `globalVars` 定义全局对象,可加入全局变量
                    globalVars: {
                        primary: '#333'
                    }
                }
        }
    },
    devServer: {
            overlay: { // 让浏览器 overlay 同时显示警告和错误
              warnings: true,
              errors: true
            },
            host: "localhost",
            port: 8080, // 端口号
            https: false, // https:{type:Boolean}
            open: false, //配置自动启动浏览器
            hotOnly: true, // 热更新
            // proxy: 'http://localhost:8080'   // 配置跨域处理,只有一个代理
            proxy: { //配置多个跨域
                "/api": {
                    target: "http://172.11.11.11:7071",
                    changeOrigin: true,
                    // ws: true,//websocket支持
                    secure: false,
                    pathRewrite: {
                        "^/api": "/"
                    }
                },
                "/api2": {
                    target: "http://172.12.12.12:2018",
                    changeOrigin: true,
                    //ws: true,//websocket支持
                    secure: false,
                    pathRewrite: {
                        "^/api2": "/"
                    }
                },
            }
        }
}

移动端调试

<script src="https://cdn.bootcss.com/vConsole/3.3.4/vconsole.min.js"></script>
 
//var vConsole = new VConsole();
1.npm install vconsole -D
2.在vue项目的main.js文件中写入

import VConsole from 'vconsole';
Vue.prototype.$vconsole = new VConsole()
//方式二:
统一局域网下,可以配置package.json
 "dev": "cross-env NODE_ENV=development webpack-dev-server --hot --port 8999 --host 192.168.0.105(主机ip)",
//在后面加上 --host加主机ip地址,然后将路径发给手机就可以了 window+R 打开 然后cmd 然后输入ipconfig 看ipv4地址
有一些需要在配置的index.js里面配置一些东西
localhost也要改成对应主机ip

Vuex

(状态管理,任一组件修改值,其他组件随之修改)

基础用法

第一步:npm i vuex -s
//store.js:
import Vue from 'vue'
import Vuex from 'vuex'

//挂载Vuex
Vue.use(Vuex)

//创建VueX对象
const store = new Vuex.Store({
    state:{
        //存放的键值对就是所要管理的状态
        name:'helloVueX'
    }
})

export default store

//在main.js中挂载Vuex
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,  //store:store 和router一样,将我们创建的Vuex实例挂载到这个vue实例中
  render: h => h(App)
})

第二步:vue组件使用

//1.State取值
第一种方式
this.$store.state.name  //使用state值

第二种方式:
import {mapState} from 'vuex'

computed:{
    ...mapState(['name'])
}
//2.mutation(变更state中的数据)
第一种方式:
const store=new Vuex.State({
state:{
    count:0
},
 mutation:{
     add(state){
         state.count++
     }
 }
})

//组件中使用
第一种方式
methods:{
handle(){
    this.$store.commit('add') //利用commit触发
}
}

第二种方式
import {mapMutations}from 'vuex'
methods:{
    ...mapMutations(['add']) //作为方法直接使用过
}
//Actions(异步任务)
const store =new Vuex.store({
mtations:{
add(state){
state.count++
  }
 }
})
actions:{
    addAsync(context,payload){
        setTimeout(()=>{
        context.commit('add',payload)//触发mutations中函数操作state数据
       },1000)
    }
}

//组件中使用
第一种方式
methods:{
  handle(){
     this.$store.dispatch('addAsync')//触发actions
  }
}
第二种方式
import {mapActions} from 'vuex'
methods:{
    ...mapActions(['addAsync'])
}
//getters(对store中得数据加工处理形成新的数据)
const store =new Vuex.store({
mtations:{
add(state){
state.count++
  }
 }
})
getters:{
  showNum:State=>{
       return '当前最新数量【'+State.count++'】'
  }
}

//组件中使用
第一种方式
this.$store.getters.showNum
第二种方式
import {mapGetters} from 'vuex'
computed:{
  ...mapGetters(['showNum'])
}

高级用法(子模块 module)

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'  // 子模块
import createPersist from 'vuex-localstorage'

Vue.use(Vuex)

// 创建vuex 构造器
export default new Vuex.Store({
    actions,
    getters,
    state,
    mutations,
    // 模块或模组
    modules:{
        user  // 引入的子模块
    },
  
})

//2.在store文件夹下创建modules文件夹,新建user.js子模块
// 每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
export default {
    state: {
        text: 'moduleA'
    },
    getters: {},
    mutations: {},
    actions: {}
}
//3.使用
<template>
    <div class="demo">
        <h1>{{getText1}}</h1>
    </div>
</template>

computed: {
    getText1(){
        return this.$store.state.user.text;
    },
}

解决页面刷新数据丢失问题

方式一:

在vue项目中用vuex来做全局的状态管理, 发现当刷新网页后,保存在vuex实例store里的数据会丢失。

//原因:
因为store里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,store里面的数据就会被重新赋值初始化

//解决思路:配合本地存储做数据持久化
1.localStorage:永久存储
2.sessionSrorage:窗口关闭销毁
//vue单页面应用,适合sessionStorage
1.sessionStorage可以保证页面打开时数据为空
2.每次打开页面localStorage存储着上一次打开页面的值,因此需要清空之前的数据

//在app.vue中
export default {
name: 'App',
created () {
//在页面加载时读取sessionStorage里的状态信息
if (sessionStorage.getItem("store") ) {
    //vuex.store的replaceState方法可以替换store的
this.$store.replaceState(Object.assign({}, this.$store.state,JSON.parse(sessionStorage.getItem("store"))))
}

//在页面刷新时将vuex里的信息保存到sessionStorage里
window.addEventListener("beforeunload",()=>{
    //监听beforeunload事件,此事件可以在页面刷新前触发
sessionStorage.setItem("store",JSON.stringify(this.$store.state))
})
}
}

方式二

const state = {
    //取值从缓存中拿
  authInfo: JSON.parse(sessionStorage.getItem("COMPANY_AUTH_INFO")) || {}
}

const getters = {
  authInfo: state => state.authInfo,
}
const mutations = {
  SET_COMPANY_AUTH_INFO(state, data) {
    state.authInfo = data
    sessionStorage.setItem("COMPANY_AUTH_INFO", JSON.stringify(data))
  }
}

//actions 模块里无需使用 sessionStorage

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  //actions,
}

vuex数据持久化(插件方式)

//1.安装vuex-persist
npm install --save vuex-persist
//2.store.js中引入
import VuexPersistence from 'vuex-persist'
//创建对象并进行配置
const vuexLocal=new VuexPersistence({
   storage:window.loaclStorage
  //storage:window.sessionStorage
})
//引入vuex插件
const store=new Vuex.Store({
    state:{...},
    mutations:{...},
    actions:{...},
    plugins:[vuexLocal.plugin]
})

vue内外边距的初始化

//APP.vue根组件中
<template>
  <div id="app">
    <router-view/>
  </div>
</template>
<style lang="less" > //注意此处不能加scoped 因为需要影响全部页面
 html,body,#app{
        height: 100%;
        width: 100%;
         padding: 0;
         margin: 0;
     
    }
</style>

vue-router(路由)

1.路由传参

1.query传参 (路径拼接方式  刷新一般不会丢失数据   name和path都可以
this.$router.push({ name:"admin",query:{id:item.id}})  
this.$router.push({ path:"/admin",query:{id:item.id}})  
接收参数:this.$route.query  
如果通过query方式传递的是数组或对象,需要通过JSON.stringify()转换

2.params传参       (只能使用name
this.$router.push({ name:"second",params:{id:item.id}})  
接收参数:this.$route.params
必须在路由中写入需要传递的参数  否则刷新会丢失数据
路由 {
   path:'/second/:id/:name', //若后面加?代表参数是可选的
   name:'second',
   component:()=>import('@/view/second')
}
3.使用props配合组件路由解耦
// 路由配置
{ 
    path: '/detail/:id',
    name: 'detail', 
    component: Detail,
    props: true // 如果props设置为true,$route.params将被设置为组件属性
} 

// 列表页(路由传参)
goDetail(row) { 
    this.$router.push({
        path: '/detail',
        query: {
            id: row.id
        }
    })
} 
-------------------------------------
// 详情页(获取传递参数)
export default {
    props: { // 将路由中传递的参数id解耦到组件的props属性上
        id: String
    },
    mounted: {
        console.log(this.id)
    }
}

2.路由跳转几种方式

this.$router.replace()
//跳转到指定URL,替换history栈中最后一个记录,点击后退会返回至上上一个页面

this.$router.push()
//跳转到指定URL,向history栈中添加一个新的记录,点击后退会返回至上一个页面

this.$router.go(1)     
//向前或向后跳转n个页面,n可正(向后跳转)可负(向前跳转)

Vue过滤器

1.全局过滤器

//在utils中定义一个filter.js
import Vue from "vue"

/** 马赛克 */                   针对姓名  两个字只显示姓;  超过两个字 显示姓名首尾,中间*
Vue.filter("MosaicName", function (str) {
    var temp
  if (str.length === 0) { 
   temp =''
  } else if (str.length > 2) {
    var temp = ""
    temp = str.substring(0, 1) + "*" + str.substring(str.length - 1, str.length)
  } else {
    temp = str.substring(0, 1) + "*"
  }
  return temp
})

      针对索引 动态隐藏 根据数量添加*号
  Vue.filter("Mosaic", function (str, preIndex, posIndex) {
    let temp = "";
    if (str && preIndex <= posIndex) {
     for (var i = 0; i < str.length; i++) {
       if (i >= preIndex && i <= posIndex) {
         temp += "*";
        } else {
          temp += str[i];
        }
      }
    } else {
      temp = str || "";
    }
    return temp.toString();
   })

//在main.js中引入
import "./utils/filter"; 

//组件中直接使用
  <span class="name">{{name | MosaicName}}</span>

Vue自定义指令

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue自定义指令</title>
</head>

<body>
    <div id="app">
        <input type="text" v-color="msg">  <!--指令的绑定值为msg-->
        <input type="text" v-focus>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.7"></script> 
    <script>

        /*   Vue.directive('color',{
              bind:function(el,binding){
                  console.log(binding.value.color);
                  // 根据指令参数设置背景色
                  el.style.backgroundColor=binding.value.color
              }
          })
   */


        // 全局自定义指令
        /*     Vue.directive('color',{
                bind(el,binding){
                    el.style.backgroundColor=binding.value.color
                }
            }) */
        var vm = new Vue({
            el: '#app',
            data: {
                msg: {
                    color: 'red'
                },
                msg2: 'hojjom',
                num: 100
            },
            // 局部自定义指令(写在实例中)
            directives: {
                color: {
                    //binding 是一个对象{//value:指令的绑定值}
                    bind(el, binding) { 
                        el.style.backgroundColor = binding.value.color 
                                            //元素颜色为绑定值的color属性值
                    }
                },
                focus: {
                    inserted(el) {
                        el.focus()
                    }
                }
            }
        });

    </script>
</body>

</html>

VUE项目美化滚动条

::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.1);
  border-radius: 0;
}

::-webkit-scrollbar {
  -webkit-appearance: none;
  width: 6px;
  height: 6px;
}

::-webkit-scrollbar-thumb {
  cursor: pointer;
  border-radius: 5px;
  background: rgba(0, 0, 0, 0.15);
  transition: color 0.2s ease;
}

组件封装使用

1.滚动词云

<!--
 * @Description:会动的词云
 * @Author: Vergil
 * @Date: 2021-08-25 14:17:45
 * @LastEditTime: 2021-08-25 17:08:15
 * @LastEditors: Vergil
-->
<template>
  <div class="wordCloud" ref="wordCloud">
  </div>
</template>
<script>
export default {
	name: 'word-cloud',
	data () {
		return {
			hotWord: ['万事如意', '事事如意 ', '万事亨通', '一帆风顺', '万事大吉', '吉祥如意', '步步高升', '步步登高', '三羊开泰', '得心应手', '财源广进', '陶未媲美', '阖家安康', '龙马精神', '锦绣前程', '吉祥如意', '生龙活虎', '神采奕奕', '五谷丰登', '马到成功', '飞黄腾达', ' 步步高升', '福禄寿禧'],
			color: [
				'#a18cd1', '#fad0c4', '#ff8177',
				'#fecfef', '#fda085', '#f5576c',
				'#fe9a8b', '#30cfd0', '#38f9d7'
			],
			wordArr: [],
			timer: null,
			resetTime: 10,
			ContainerSize: ''
		};
	},
	mounted () {
		this.init();
	},
	methods: {
		init () {
			this.dealSpan();
			this.initWordPos();
			this.render();
		},
		dealSpan () {
			const wordArr = [];
			this.hotWord.forEach((value) => {
				// 根据词云数量生成span数量设置字体颜色和大小
				const spanDom = document.createElement('span');
				spanDom.style.position = 'relative';
				spanDom.style.display = 'inline-block';
				spanDom.style.color = this.randomColor();
				spanDom.style.fontSize = this.randomNumber(15, 30) + 'px';
				spanDom.innerHTML = value;
				spanDom.local = {
					position: {
						// 位置
						x: 0,
						y: 0
					},
					direction: {
						// 方向 正数往右 负数往左
						x: 1,
						y: 1
					},
					velocity: {
						// 每次位移初速度
						x: -0.5 + Math.random(),
						y: -0.5 + Math.random()
					},
				};
				this.$refs.wordCloud.appendChild(spanDom);
				wordArr.push(spanDom);
			});
			this.wordArr = wordArr;
		},
		randomColor () {
			// 获取随机颜色
			var colorIndex = Math.floor(this.color.length * Math.random());
			return this.color[colorIndex];
		},
		randomNumber (lowerInteger, upperInteger) {
			// 获得一个包含最小值和最大值之间的随机数。
			const choices = upperInteger - lowerInteger + 1;
			return Math.floor(Math.random() * choices + lowerInteger);
		},
		render () {
			if (this.resetTime < 100) {
				this.resetTime = this.resetTime + 1;
				this.timer = requestAnimationFrame(this.render.bind(this));
				this.resetTime = 0;
			}
			this.wordFly();
		},
		wordFly () {
			this.wordArr.forEach((value) => {
				// 设置运动方向 大于边界或者小于边界的时候换方向
				if (value.local.realPos.minx + value.local.position.x < this.ContainerSize.leftPos.x || value.local.realPos.maxx + value.local.position.x > this.ContainerSize.rightPos.x) value.local.direction.x = -value.local.direction.x;
				if (value.local.realPos.miny + value.local.position.y < this.ContainerSize.leftPos.y || value.local.realPos.maxy + value.local.position.y > this.ContainerSize.rightPos.y) value.local.direction.y = -value.local.direction.y;
				value.local.position.x += value.local.velocity.x * value.local.direction.x;
				value.local.position.y += value.local.velocity.y * value.local.direction.y;
				// 给每个词云加动画过渡
				value.style.transform = 'translateX(' + value.local.position.x + 'px) translateY(' + value.local.position.y + 'px)';
			});
		},
		initWordPos () {
			// 计算每个词的真实位置和容器的位置
			this.wordArr.forEach((value) => {
				value.local.realPos = {
					minx: value.offsetLeft,
					maxx: value.offsetLeft + value.offsetWidth,
					miny: value.offsetTop,
					maxy: value.offsetTop + value.offsetHeight
				};
			});
			this.ContainerSize = this.getContainerSize();
		},
		getContainerSize () {
			// 判断容器大小控制词云位置
			const el = this.$refs.wordCloud;
			return {
				leftPos: {
					// 容器左侧的位置和顶部位置
					x: el.offsetLeft,
					y: el.offsetTop
				},
				rightPos: {
					// 容器右侧的位置和底部位置
					x: el.offsetLeft + el.offsetWidth,
					y: el.offsetTop + el.offsetHeight
				}
			};
		}
	},
	destroyed () {
		// 组件销毁,关闭定时执行
		cancelAnimationFrame(this.timer);
	},
};
</script>
<style lang="less" scoped>
.wordCloud{
   width:100%;
   height:100%;
}
</style>

2.封装按钮button

//src/components下创建my-button.vue
<template>
  <button class="my-button ellipsis" :class="[size, type]">
    <slot />
  </button>
</template>
<script>
export default {
  name: 'MyButton',
  props: {
    size: {
      type: String,
      default: 'middle'
    },
    type: {
      type: String,
      default: 'default'
    }
  }
}
</script>
<style scoped lang="less">
.my-button {
  margin-right: 5px;
  appearance: none;
  border: none;
  outline: none;
  background: #fff;
  text-align: center;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
}
.ellipsis {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}
.large {
  width: 240px;
  height: 50px;
  font-size: 16px;
}
.middle {
  width: 180px;
  height: 50px;
  font-size: 16px;
}
.small {
  width: 100px;
  height: 32px;
  font-size: 14px;
}
.mini {
  width: 60px;
  height: 32px;
  font-size: 14px;
}
.default {
  border-color: #e4e4e4;
  color: #666;
}
.primary {
  border-color: #27ba9b;
  background: #27ba9b;
  color: #fff;
}
.plain {
  border-color: #27ba9b;
  color: #27ba9b;
  background: lighten(#27ba9b, 50%);
}
.gray {
  border-color: #ccc;
  background: #ccc;
  color: #fff;
}
</style>
//使用

<template>
  <div class="home-banner">
    <my-button type="default" size="mini">默认</my-button>
    <my-button type="plain" size="mini">空心</my-button>
    <my-button type="primary" size="mini">实心</my-button>
    <my-button type="gray" size="mini">灰色</my-button>
    <br /><br />
    <my-button type="default" size="mini">迷你</my-button>
    <my-button type="plain" size="small">小号</my-button>
    <my-button type="primary" size="middle">中号</my-button>
    <my-button type="gray" size="large">大号</my-button>
  </div>
</template>

<script>
import MyButton from '@/components/my-button.vue'
export default {
  name: 'App',
  components: {
  	MyButton
  },
  setup() {
    return {}
  }
}
</script>

<style lang="less">
.home-banner {
  margin: 100px auto;
  width: 700px;
  height: 300px;
}
</style>

3.侧滑组件

npm install @hyjiacan/vue-slideout //可以使用slideout组件

4.表单封装

<!--1.封装表单-->
<template>
  <div>
    <el-form ref="ruleForm" class="demo-ruleForm" :model="form" :label-width="formData.labelWidth" :inline="formData.inline" :rules="formData.rules" :size="formData.size" :label-position="formData.labelPosition">
      <el-form-item v-for="(item, index) in formData.formItem" :key="index" :label="item.label" :prop="item.prop">
        <!-- 文本框 -->
        <el-input v-if="item.type === 'text'" v-model="form[item.prop]" :placeholder="item.placeholder" :disabled="item.isDisabled" />

        <!-- 文本框支持模糊查询 -->
        <el-select v-if="item.type==='remote'" v-model="form[item.prop]" filterable remote reserve-keyword :placeholder="item.placeholder" :remote-method="remoteMethod" :loading="loading">
          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
          </el-option>
        </el-select>

        <!-- 密码框 -->
        <el-input v-if="item.type === 'password'" v-model="form[item.prop]" type="password" :disabled="item.isDisabled" />
        <!-- 单选框 -->
        <el-radio-group v-if="item.type==='radio'" v-model="form[item.prop]">
          <el-radio v-for="item in item.options" :key="item.value" :label="item.value">
            {{ item.name }}
          </el-radio>
        </el-radio-group>
        <!-- 单选按钮 -->
        <el-radio-group v-if="item.type==='radioButton'" v-model="form[item.prop]" :disabled="item.isDisabled">
          <el-radio-button v-for="item in item.options" :key="item.value" :label="item.value">
            {{ item.name }}
          </el-radio-button>
        </el-radio-group>
        <!-- 多选框组 -->
        <el-checkbox-group v-if="item.type==='checkbox'" v-model="form[item.prop]">
          <el-checkbox v-for="item in item.options" :key="item.value" :disabled="item.isDisabled" :label="item.value">
            {{item.name}}
          </el-checkbox>
        </el-checkbox-group>
        <!-- 下拉框 -->
        <el-select v-if="item.type==='select'" v-model="form[item.prop]" :multiple="item.multiple" collapse-tags clearable :disabled="item.isDisabled" :placeholder="item.placeholder">
          <el-option v-for="item in item.options" :key="item.value" :label="item.name" :value="item.value" :disabled="item.isDisabled" />
        </el-select>
        <!-- 联级面板 -->
        <el-cascader v-if="item.type==='cascader'" v-model="form[item.prop]" :options="item.options" :props="item.isMore" clearable />
        <!-- 开关 -->
        <el-switch v-if="item.type==='switch'" v-model="form[item.prop]" />
        <!-- 日期选择器 -->
        <el-date-picker v-if="item.type==='date'" v-model="form[item.prop]" type="date" value-format="yyyy-MM-dd" placeholder="选择日期" />
        <!-- 时间选择器 -->
        <el-time-picker v-if="item.type==='time'" v-model="form[item.prop]" placeholder="请选择时间" />
        <!-- 日期时间选择器 -->
        <el-date-picker v-if="item.type==='dateTime'" v-model="form[item.prop]" type="datetime" placeholder="选择日期时间" />
        <!-- 日期和时间范围选择器  -->
        <el-date-picker v-if="item.type==='datetimerange'" v-model="form[item.prop]" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit('ruleForm')">
          查询
        </el-button>
        <el-button @click="resetForm('ruleForm')">
          重置
        </el-button>
      </el-form-item>
    </el-form>

    <el-button type="primary">+{{btnName}}</el-button>
  </div>

</template>

<script>
export default {
  props: {
    btnName: {
      type: String,
    },
    formData: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      form: {},
      options: [],
      loading: false,
    };
  },
  created() {
    this.bindValue();
  },
  methods: {
    onSubmit(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          console.log(this.form);
          alert("发送请求去");
        } else {
          return false;
        }
      });
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
    bindValue() {
      const obj = {};
      this.formData.formItem.forEach((item, index) => {
        // 这里不能写成this.form = obj  因为传递的不是值,而是引用,他们指向了同一个空间!
        obj[item.prop] = item.value;
      });
      this.form = { ...obj };

      console.log(this.form["screenName"]);
    },

    //模糊查询
    remoteMethod(query) {
      console.log(query);
      if (query !== "") {
        this.loading = true;
        setTimeout(() => {
          //发送请求获取数据
          var list = [
            {
              label: "你好",
              value: 0,
            },
            {
              label: "哈哈哈",
              value: 1,
            },
            {
              label: "我是谁",
              value: 2,
            },
            {
              label: "吱吱吱",
              value: 3,
            },
          ];
          this.loading = false;
          this.options = list.filter((item) => {
            return item.label.toLowerCase().indexOf(query.toLowerCase()) > -1;
          });
        }, 200);
      } else {
      }
    },
  },
};
</script>

<style scoped lang="less">
</style>


<!--2.使用-->
<template>
  <div>
    <form-icon :form-data="formData" :btnName="btnName" />
  </div>
</template>

<script>
import formIcon from "@/components/form/Ele_form";
import { baseRules } from "@/utils/rules";
export default {
  components: {
    formIcon,
  },
  data() {
    return {
      btnName: "新增横幅",
      formData: {
        rules: baseRules,
        labelWidth: "100px",
        inline: true,
        labelPosition: "right",
        size: "medium",
        formItem: [
          {
            type: "text",
            label: "横幅名称",
            isDisabled: false,
            placeholder: "请输入名称",
            prop: "bannerName",
            value: "",
            required: true,
          },
            {type: 'password', label: '密码', isDisabled: false, placeholder: '请输入密码', prop: 'password', value: '', required: true},
            {type: 'radio', label: '性别', isDisabled: false, prop: 'sex', value: '', options: [{name: '男', value: '1'}, {name: '女', value: '0'}]},
             {type: 'switch', label: '状态', isDisabled: false, prop: 'status', value: '0'},
             {
              type: 'radioButton',
              isDisabled: true,
              label: '选择城市',
              prop: 'city',
              value: 'huaian',
              options: [
                {name: '上海', value: 'shanghai'},
                {name: '北京', value: 'beijing'},
                {name: '淮安', value: 'huaian'}
              ]
            },
            {
              type: 'checkbox',
              isDisabled: false,
              label: '爱好',
              prop: 'hoppies',
              value: [],
              options: [
                {name: '游戏', value: 'LOL', isDisabled: true},
                {name: '健身', value: 'fitness', isDisabled: false},
                {name: '娱乐', value: 'bath', isDisabled: false},
                {name: 'Code', value: 'code', isDisabled: true}
              ]
            },
          {
            type: "select",
            isDisabled: false,
            // 是否开启多选
            multiple: false,
            label: "横幅类型",
            placeholder: "选择类型",

            prop: "bannerType",
            value: [],
            options: [
              { name: "胶囊横幅", value: 0, isDisabled: false },
              { name: "异形横幅", value: 1, isDisabled: false },
              { name: "常规横幅", value: 2, isDisabled: false },
            ],
          },
          {
            type: "select",
            isDisabled: false,
            // 是否开启多选
            multiple: false,
            label: "发布状态",
            placeholder: "选择状态",
            prop: "status",
            value: [],
            options: [
              { name: "生效中", value: 0, isDisabled: false },
              { name: "已启用", value: 1, isDisabled: false },
              { name: "已过期", value: 2, isDisabled: false },
              { name: "未生效", value: 3, isDisabled: false },
            ],
          },
          {
            type: "select",
            isDisabled: false,
            // 是否开启多选
            multiple: false,
            label: "生效期限",
            placeholder: "选择期限",

            prop: "effectivePeriod",
            value: [],
            options: [
              { name: "长期有效", value: 0, isDisabled: false },
              { name: "三个月", value: 1, isDisabled: false },
              { name: "一个月", value: 2, isDisabled: false },
              { name: "自定义期限", value: 3, isDisabled: false },
            ],
          },
           {
             type: 'cascader',
             label: '地址',
             prop: 'dizhi',
             isMore: {multiple: false},
             isDisabled: false,
             value: [],
             options: [
               {
                 value: 'js',
                 label: '江苏省',
                 children: [
                   {
                     value: 'nanjing',
                     label: '南京市'
                   },
                   {
                     value: 'suzhou',
                     label: '苏州市'
                   },
                   {
                     value: 'wuxi',
                     label: '无锡市'
                   },
                   {
                     value: 'huaian',
                     label: '淮安市',
                     children: [
                       {
                         value: 'pjpq',
                         label: '清江浦区'
                       },
                       {
                         value: 'hyq',
                         label: '淮阴区'
                       }
                     ]
                   }
                 ]
               },
               {
                 value: 'sh',
                 label: '上海市',
                 children: [
                   {
                     value: 'pudong',
                     label: '浦东新区'
                   },
                   {
                     value: 'xuhui',
                     label: '徐汇区'
                   },
                   {
                     value: 'minhang',
                     label: '闵行区'
                   },
             {
                   value: 'songjiang',
                   label: '松江区',
                   children: [
                     {
                       value: 'dongjing',
                       label: '洞泾'
                     },
                     {
                       value: 'jiuting',
                       label: '九亭'
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {type: 'date', label: '日期', prop: 'starTime', value: ''},
          {type: 'time', label: '时间', prop: 'time', value: ''},
          {type: 'dateTime', label: '日期时间', prop: 'dateTime', value: ''},
          {type: 'datetimerange', label: '范围选择器', prop: 'datetimerange', value:           ''}
        ],
      },
    };
  },
};
</script>

<style lang="scss" scoped>
</style>

5.弹窗封装

<template>
  <div id="Popup">
    <van-overlay :show="maskShow">
      <div @click.stop class="wrapper">
        <div class="block">
          <div class="topbanner"></div>
          <div class="title">温馨提示</div>
          <div class="content" v-html="btnInfos.info"></div>
          <van-button v-if="btnInfos.btnName" @click="close(btnInfos.url)" class="btn" round>返回</van-button>
          <van-button v-else @click="submit" class="btn" round>点击查看</van-button>
        </div>

        <img v-if="!btnInfos.btnName" @click="maskShow=false" alt class="close" src="../assets/closePopup.png" />
      </div>
    </van-overlay>
  </div>
</template>

<script>
export default {
  props: {
    btnInfos: {
      type: Object,      //传递的值
    },
  },
  data() {
    return {
      maskShow: true,
    };
  },
  methods: {
    submit() {
      this.$router.push(this.btnInfos.url); //跳转的逻辑
    },
    close(url) {
     //返回的逻辑
    },
  },
};
</script>

<style lang="less" scooped>
#Popup {
  .wrapper {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    height: 100%;
    text-align: justify;
  }

  .block {
    width: 311px;
    background: #ffffff;
    border-radius: 12px;
    overflow: hidden;
    padding-bottom: 24px;
    margin-bottom: 56px;
    .topbanner {
      height: 80px;
      border-radius: 12px 12px 0 0;
      background: url("../assets/submitDialog.png");
    }
    .title {
      font-size: 16px;
      font-family: PingFangSC-Semibold, PingFang SC;
      font-weight: 600;
      color: rgba(0, 0, 0, 0.86);
      text-align: center;
      margin: 25px 0 13px;
    }
    .content {
      margin: 0 32px 26px;
      font-size: 14px;
      font-family: PingFangSC-Regular, PingFang SC;
      font-weight: 400;
      color: rgba(0, 0, 0, 0.86);
      line-height: 20px;
    }
    .btn {
      width: 248px;
      border: none;
      margin-left: 50%;
      transform: translateX(-50%);
      font-size: 16px;
      font-family: PingFangSC-Semibold, PingFang SC;
      font-weight: 600;
      color: #ffffff;
      background: linear-gradient(180deg, #ffb900 0%, #ff8200 100%);
    }
  }
  .close {
    width: 24px;
    height: 24px;
  }
}
</style>

拓展:css变量

body {
  --color: red;  /** 声明全局变量  **/
}
h1 {
  color: var(--color); /** 这里获取到的是全局声明的变量,值为red **/
}
div1 {
  --color: blue;   /** 局部变量 **/
  color: var(--color); /** 这里获取到的是局部声明的变量,值为blue **/
}
div2 {
  width: var(--width, 100px)/** 如果未定义--width,则取第二个参数值(默认参数) **/
}
//在vue项目中进行使用css变量(旧写法)
<template>
  <h1>Vue</h1>
</template>

<script>
export default {
  data () {
    return {
        //data中进行声明
      border: '1px solid black',
      color: 'red'
    }
  }
}
</script>
//首先要在<style>标签中写个vars="{}",再在大括号里写上你在data中声明过的值。
<style vars="{ border, color }" scoped>
h1 {
  color: var(--color);  /* 进行使用 */
  border: var(--border);
}
</style>
scoped属性限制下,使用全局css变量可以使用global:,如下:
color:var(--global:color)
<template>
  <h1> shit! </h1>
</template>

<script>
export default {
  data () {
    return {
      color: 'yellow',
      font:{
          weight:100
    }
    }
  }
}
</script>

<style>
h1 {
  color: v-bind(color),
  font-weight:v-bind('font:weight')
/* 第一:这次更新改变了写法,在<style>标签中不需要再写vars="{ xxx }"了,在 CSS 代码中也不需要再写原生的 CSS 变量引用了(var(--xxx)),取而代之的是v-bind()这个函数,注意 ⚠️ 这个函数虽然跟 JS 里的v-bind很像,但只能用在 CSS 中  */
}
</style>

多个线上环境配置方案

问题: 一般情况下项目开发线上环境只存在一个,而我司情况比较特殊, 项目需在各个厂区上线, 各个厂区使用的数据库, 域名端口皆不相同, 若配置多个线上环境, 每次项目更新都需打包多份代码重新部署

//解决方案: 我司通过字符串拼接的方式对项目的基本地址进行拼接, 在入口函数main.js中:
const host = window.location.host  // 获取当前主机名称
const protocol = window.location.protocol // 获取当前URL协议
axios.defaults.baseURL = protocol + '//' + host // 对基本地址进行拼接

利用vscode插件添加与删除console.log

shift+ctrl+l(选中变量名)
shift+ctrl+d

打包去除console.log

chainWebpack(config) {
    config.optimization.minimizer('terser').tap((args) => {
      args[0].terserOptions.compress.drop_console = true
      return args
    })
}

element ui相关

1.el-button防抖

//在main.js中
// 提交以后禁用按钮一段时间,防止重复提交
import Vue from 'vue'
Vue.directive('noMoreClick', {
  inserted(el, binding) {
    el.addEventListener('click', e => {
      el.classList.add('is-disabled')
      el.disabled = true
      setTimeout(() => {
        el.disabled = false
        el.classList.remove('is-disabled')
      }, 2000)//我这里设置的是2000毫秒也就是2秒
    })
  }
})
//使用
<el-button v-no-more-click type="primary" @click="submitForm('ruleForm')">确 定</el-button>

---------------------------
    若出现报错 可以看下eslint
    1: vue.config.js 中增加 lintOnSave: false
    2package.json -> eslintConfig ->rules ->
        {
	    "no-": "off",
      "no-debugger":"off",
      "no-console": "off",
      "no-empty":"off",
      "no-unused-vars":"off"
     }
    3: 新建 .eslintrc.json
    {
    "rules": {
        "no-unused-vars": 0
       }
    }

2.dialog封装

//子组件
<template>
  <div>
    <el-dialog
      title="新增"
      :visible.sync="show"
      @close="$emit('update:visible', false)"
    >
      <el-form>
        <el-form-item label="姓名">
          <el-input v-model="name" />
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>
</template>
<script>
    export default {
        props: {
            visible: {
              type: Boolean,
              default: false
            }
          },
          data() {
            return {
              name: ' ',
              show: this.visible,
            };
          },
          watch: {
            visible(val) {
              this.show = val;
            }
          }
      }
</script>

//父组件
<template>
    <div>
        <button @click="handleAdd">打开/关闭</button>
        <addDialog :visible.sync="addshow" />
    </div>
</template>
<script>
    import addDialog from "./addDialog.vue";
    export default {
        components: { addDialog },
        data() {
          return {
            addshow: false,
          };
        },
        methods: {
          handleAdd() {
            this.addshow = true;
          }
        }
  };
</script>

实现的思路是 父组件传递的参数添加 .sync 修饰,子组件中关闭弹框时使用 $emit('update:visible', false) 这样更改父组件中传递的 visible 的值,子组件中watch 到visible改变,就改变中间值show达到关闭的目的
--------------------------------------------------------
<addDialog :visible.sync="addshow" />.sync 是下面代码的语法糖
<addDialog :visible="addshow" @update:visible="newVal => addshow = newVal"/>


3.分页封装

// 分页子组件
<template>
  <div class="paging">
    <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" background
      layout="total, sizes, prev, pager, next, jumper" :total="total">
    </el-pagination>
  </div>
</template>

<script>
export default {
  name: "Pagination",
  props: ['total','pageSize','currentPage'],
  methods: {
    handleSizeChange(val) {
        this.$emit('pagination2', val )
    },
    handleCurrentChange(val) {
        this.$emit('pagination1', val)
    },
  },
};
</script>
--------------------------------------------------------------------------
父组件
<template>
      <!-- 分页按钮 -->
      <Pagination :total="total" :currentPage="form.pageNum" :pageSize="form.pageSize" @pagination2="handleSizeChange" @pagination1="handleCurrentChange">
      </Pagination>
      <!-- /分页按钮 -->
</template>
<script>
    import Pagination from "@/components/pagination/index.vue";
    export default {
  components: {
    Pagination
  },
  data() {
    return {
      total: 0,
      //表单项
      form: {
         ...
        pageNum: 1,
        pageSize: 10,
      }    
      },
    };
  },
  methods: {
  //改变每页条数
    handleSizeChange(val) {
      this.form.pageSize = val;
      this.search();//列表查询
    },
    //改变当前页
    handleCurrentChange(val) {
      this.form.pageNum = val;
      this.search();//列表查询
    },
};
</script>

4.表格序号

//方法1:
<el-table-column min-width="50px" fixed  label="序号" align="center">
     <template slot-scope="scope" >
        <span v-if="scope.$index-1<0" >合计</span>
        <span v-else> {{(form.pageNum - 1) * form.pageSize + scope.$index }}</span>
     </template>
</el-table-column>

//方法2:建议修改数据源 遍历添加序号  
export default {
    data(){
        return {
            form:{
                pageSize: 10,
                pageNum: 1,
            }
        }
    },
    created(){
         this.getList()
    },
    methods:{
        getList(){
           axios({
                    method: 'post',//请求方法
                    data: this.form,
                    url: '后台接口地址',
                }).then(res => {
                //执行成功后代码处理
                       this.list=res.data.data.map((item,i)=>{
                       // 当前项索引= 当前页*每页展示数量+1
                       item.index=this.form.pageNum-1*this.form.pageSize+i+1
                       return item
                 })
            })
        }
    }
}

5.表单规则rules封装

//element表单验证规则
export const baseRules={
    userId: [
        {
          pattern: /[0-9a-zA-Z]|[\u4e00-\u9fa5]/im, //可以写正则表达式呦呦呦
          message: "不允许输入特殊字符",
          trigger: "blur",
        },
      ],
    phone: [
        {
          pattern: /^1[34578]\d{9}$/, //可以写正则表达式呦呦呦
          message: "目前只支持中国大陆的手机号码",
          trigger: "blur",
        },
      ],
    date: [
        {
        required:true,
          message: "日期必填",
          trigger: "blur",
        },
      ],

}


//2使用

<template>
  <div id="Carbon">

    <el-form ref="form" :inline="true" :model="form" :rules="rules">
      <el-row>
        <el-form-item label="用户ID"  prop="userId">
          <el-input v-model="form.userId" maxlength="50" placeholder="请输入用户ID">             </el-input>
        </el-form-item>

        <el-form-item label="手机号码"  prop="phone">
          <el-input v-model="form.phone" placeholder="请输入手机号码"></el-input>
        </el-form-item>
      </el-row>
      <el-row>
        <el-form-item label="选择日期" prop="date">
          <el-date-picker :picker-options="pickerOptions" v-model="form.date" value-format="yyyy-MM-dd HH:mm:ss" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期">
          </el-date-picker>
        </el-form-item>
      </el-row>

    </el-form>

</template>

<script>
import { baseRules } from "@/utils/rules"; //表单验证规则
export default {
  name: "",
  components: {
  },
  data() {
    return {
       // 校验规则
       rules: baseRules,
        form: {
        userId: "", //用户ID
        phone: "", //手机号码
        date: "", //日期
        startTime: "", //日期选择开始时间
        endTime: "", //日期选择结束时间
        pageNum: 1,
        pageSize: 10,
      },
      minDate: "",
      maxDate: "",
      pickerOptions: {
        onPick: ({ maxDate, minDate }) => {
          this.minDate = minDate;
          this.maxDate = maxDate;
        },
        disabledDate: (time) => {
          //查询时间跨度为90天
          if (this.minDate) {
            let range = 90 * 24 * 3600 * 1000;
            return (
              time.getTime() > Date.now() ||
              time.getTime() > this.minDate.getTime() + range ||
              time.getTime() < this.minDate.getTime() - range
            );
          }
          return time.getTime() > Date.now();
        },
      },
    }
  }
}
</script>

6.表单label加图标和气泡框

<el-form-item>
   <span slot="label">
    <span>label值</span>
     <el-popover placement="top-start" title="标题" width="200"trigger="hover"content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。">
    <i  class="el-icon-question" slot="reference"></i>
  </el-popover>
    </span> 
   <el-input></el-input>
</el-form-item>

7.日期时间选择器空值时间范围跨度

 
  <el-date-picker  v-model="publishDate" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss"
            :picker-options="pickerOptions">
          </el-date-picker>


pickerOptions: {
        onPick: ({ maxDate, minDate }) => {
          this.minDate = minDate;
          this.maxDate = maxDate;
        },
        disabledDate: (time) => {
          let skipDays = 30 * 24 * 3600 * 1000;   //跨度天数
          let curDate = new Date().toString(); // 当前时间戳转为字符串
          let curDateYear = new Date().getFullYear(); // 当前时间的年份
          let oneYearAgoDate = curDate.replace(curDateYear, curDateYear - 1); // 字符串年份替换为一年前
          let oneYear = new Date(oneYearAgoDate).getTime(); //一年前字符串转为时间戳
          if (this.minDate) {
            return (
              time.getTime() > Date.now() ||
              time > new Date(this.minDate.getTime() + skipDays) ||
              time < new Date(this.minDate.getTime() - skipDays)
            );
          }
          //未选择限制最近一年内
          return time.getTime() > Date.now() || time.getTime() < oneYear;
        },
      },
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值