Vue项目学习知识点汇总(持续更新)

745 阅读9分钟

包含一些组件的实现,一些问题的解决方法,一些技术点总结

一,面包屑的实现

组件主要使用的是Element-ui中的breadcrumb来实现,组件中的数据主要来自项目路由信息 :

在配置路由时,加上meta信息配置,code如下:

{
      path: '/index',
      component: () => import('@/views/index'),
      name: 'index',
      meta: {
        title: '首页'
      },
      children:[
        {
            path:'/list',
            component: () => import('@/views/list'),
            name: 'list',
            meta:{
                title: '列表'
            }
           
        }
      ]
}

在breadcrumb文件中,

 <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item v-for="item in list" :key="item.path">
        <span class="no-redirect" >{{item }}</span>
      </el-breadcrumb-item>
    </el-breadcrumb>
    
    export default {
        data(){
            return {
                list: null
            }
        },
        method:{
            getList(){
                  //this.$route.matched可以获取当前路由下的所有信息,是一个数组
                  const matched = this.$route.matched.filter(item=>{
                  if (item.name && item.meta.title) { //过滤掉没有配置title的路由
                         return true;
                    }
                  )
                  this.list = matched
            }
        }
    }

二,git pull 时出现错误: Your local changes to the following files would be overwritten by merge:

这是本地代码和仓库中的代码出现了冲突

  • 解决方法:
  • 第一步:git stash 将工作区恢复到上次提交的内容,同时备份本地所做的修改 备份当前的工作区的内容,从最近的一次提交中读取相关内容,让工作区保证和上次提交的内容一致。同时,将当前的工作区内容保存到Git栈中
  • 第二步:git pull 可以正常拉取代码
  • 第三步: git stash pop 将之前本地修改应用到当前工作区 从Git栈中读取最近一次保存的内容,恢复工作区的相关内容。由于可能存在多个Stash的内容,所以用栈来管理,pop会从最近的一个stash中读取内容并恢复

git stash的相关命令:

  • git stash list :显示Git栈内的所有备份,可以利用这个列表来决定从那个地方恢复
  • git stash clear: 清空Git栈。此时使用gitg等图形化工具会发现,原来stash的哪些节点都消失了。

三,富文本编辑器在mounted中初始化,切换组件不显示(使用的是tinymce编辑器)

  • 解决方法: 在destroyed中执行编辑器的销毁函数
destroyed() {
    const editor = window.tinymce.get('myeditor');
    editor.destroy();
  }

四,修改element UI组件样式


外层样式 >>> 内层 样式 {
    
}
.el-setting-margin >>> .el-tooltip-icon_q {

}

五, watch的高级用法

  • immediate属性: immediate:true代表如果在 wacth 里声明了 监听的对象 之后,就会立即先去执行里面的handler方法,如果为 false,不会在绑定的时候就执行。
  • deep属性: deep,默认值是 false,代表是否深度监听
<div>
    <p>obj.a: <input type="text" v-model="obj.a"></p>
</div>


new Vue({
  el: '#root',
  data: {
    obj: {
      a: 123
    }
  },
  watch: {
    obj: {
      handler(new, old) {
         console.log('obj.a');
      },
      immediate: true
    }
  } 
  当obj.a发生改变时不会触发监听handler,只有给obj重新赋值才能触发,如果使用deep,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样的开销较大,每一个属性都加上了监听
  
  watch: {
  obj: {
    handler(new, old) {
      console.log('obj.a');
    },
    immediate: true,
    deep: true
  }
} 

最优化的方法是具体到属性加上监听:
watch: {
  'obj.a': {
    handler(new, old) {
      console.log('obj.a changed');
    },
    immediate: true
  }
}

六, 导出excel表格功能实现(Chrome,firefox)

UI:
<el-button type="primary" @click="handleDownload()">导出excel</el-button>
js:
handleDownload(){
    //向后端发送请求返回blob类型的数据
      const excelParams = {}
      axios({
        method: 'get',
        url: `url`,
        params: excelParams,
        responseType: 'blob',  //一定要带上
      )}.then(res=>{
            const content = res.data; //拿到返回的数据
            //使用moment对日期进行获取和格式化,生成文件名(之后会总结moment的使用)
             const fileName = `myfile_${moment(new Date()).format('YYYYMMDD')}_${moment(new Date()).format('HHmmss')}.xls`;
            if ('download' in document.createElement('a')) {
              const a = document.createElement('a');  //利用a标签的download属性实现下载
              a.download = fileName;    //
              a.href = URL.createObjectURL(content); // 创建url预览地址
              document.body.appendChild(a);
              a.click(); // a标签点击
              URL.revokeObjectURL(a.href); // 释放URL 对象,使性能优秀
              document.body.removeChild(a); 
            }
      })
}


七, git 上传提交信息同步到issue中?

在github上首先创建了一个issue,然后根据issue序号创建了一个分支,将分支同步到了本地,按照正常操作上传,发现在code中显示了提交信息,在issue中并没有信息,解决方法: 在提交时,在提交信息中,带上issue的序号,如: issue序号是: #50 则提交信息是: git commit -m '#50 xxxxx' 然后打开issue,发现信息已经出现

八, watch和computed的区别及应用场景?

  • 区别: watch中的函数是不需要调用的, watch监听的属性发生改变时,会自动调用 其中的函数,在数据变化时执行异步或开销较大的操作时使用

computed内部的函数调用的时候不需要加(),函数被当作属性来使用,函数中必须用return返回最终结果,computed 的结果会被缓存,除非依赖的属性发生变化才会重新计算,当依赖的属性没有发生变化时,会从缓存中取得结果

  • 应用场景:
  • computed     当一个属性受多个属性影响的时候就需要用到computed 最典型的例子: 购物车商品结算的时候
  • watch 当一条数据影响多条数据的时候就需要用watch 搜索数据 监听路由的变化 watch: { $route(){ xxxx } }

九, js-cookie的使用?

npm 为我们封装好了cookie的插件:

  • 下载js-cookie: npm i js-cookie -S

  • 引用 import cookie from ‘js-cookie’

          * 设置cookie:cookies。set('name','val,{expires:1})
          * 获取cookie: cookies.get('name')
          * 删除cookie: cookies.remove('name',{path:'/'})
    

十,vue中国际化的使用(包含ElementUI)

此处使用了中文和英文两种语言 在项目src文件下:
新建文件夹lang
lang---
en--- 放英文的文件
zh--- 放中文的文件
例如: export const status = { s1: '所有状态', s2: '已解决', s3: '未解决', };
index.js 配置语言的文件
index.js:

            import Vue from 'vue';
            import VueI18n from 'vue-i18n';
            import Cookies from 'js-cookie';
            import elementEnLocale from 'element-ui/lib/locale/lang/en'; // element-ui lang
            import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN';// element-ui lang
            import enLocale from './en/index';
            import zhLocale from './zh/index';
            
            Vue.use(VueI18n);
            const messages = {
                en: {
                 ...enLocale,
                 ...elementEnLocale
                 },
                 zh: {
                ...zhLocale,
                ...elementZhLocale
                },
            }
            const getDefaultLanguage = function() {
              const lans = ['zh', 'en'];
              const langString = navigator.language || navigator.userLanguage;
              const lang = langString.substr(0, 2);
              return lans.indexOf(lang) >= 0 ? lang : 'en';
            };
            const defaultLang = getDefaultLanguage();
            const i18n = new VueI18n({
              locale: Cookies.get('language') || defaultLang,
              messages
            });
            // locale 设置语言
            // messages 语言包
            // navigator.language 获取浏览器语言
            // navgator.userLanguage 获取操作系统语言
            
            export default i18n;
            
    在项目的main.js 文件中:
            import i18n from './lang'; // Internationalization
            import ElementUI from 'element-ui';
            Vue.use(ElementUI, {
                  i18n: (key, value) => i18n.t(key, value)  //在组件中通过$t('')渲染文字
            });
            
            new Vue({
              el: '#app',
              router,
              store,
              i18n,
              render: h => h(App)
            });
            
            在组件中切换语言:   this.$i18n.locale = lang;  修改语言
            
            在组件中使用: 
            <span>{{$t('status.s1')}}</span>
           

十一,组件内导航beforeRouteUpdate的使用

使用场景:组件复用,路由跳转
但这个守卫并不会在不同路由共用同一组件时触发

beforeRouteUpdate(to,from,next){
    //在当前路由改变,但是该组件被复用时调用
    //例如:从list/1跳转到list/2,渲染的都是list组件
    //可以访问实例的this
    
}

十二,vue 中 .sync, .native修饰符的使用?

  • .sync
    作用: 对传递给子组件的prop数据进行双向绑定
父组件:
<Hello :msg:sync="msg"></Hello>

data(){
    return{
        msg:'i am test'
    }
}

子组件:
<span @click="handleClick()">{{}}</span>

props:{
    msg:{
        type:string,
        default:''
    }
}
handleClick(){
    this.$emit('update:msg','我是点击后的信息')
}
  • .native
    作用:在父组件中给子组件绑定一个原生事件,使子组件变成普通的html标签,不加native修饰符,事件无法触发
    (给普通标签加native无效,仅用于自定义组件和element等)
父组件中:
<MyButton @click.native="handleClick()"/> //给子组件绑定一个点击事件,如果没有native事件不执行
 handleClick() {
      console.log('ccccc');
    },

子组件:
  <el-button type="purple">子组件点击</el-button>

十三,vue中Clipboard使用?

Clipboard: 现代化的拷贝文字, 拷贝文字不应当是一件困难的事. 不需要过多繁杂的配置或者下载很多脚本文件. 最重要的,它不应该依赖flash或者其他框架,应该保持简洁。这就是创造clipboard.js的原因和目的
官方文档http://www.clipboardjs.cn/

  • 用于复制(浏览器支持)
template:
 <textarea id="bar" v-model="textarea"/>
 <el-button type="white" @click="handleCopy(textarea, $event)">copy it</el-button>
script:
data(){
    return{
        textarea:'要复制的内容'
    }
}
handleCopy(text,event){
     const clipboard = new Clipboard(event.target, {
        text: () => text
      });
      clipboard.on('success', (e) => {
        this.success('复制成功');
        clipboard.off('error');
        clipboard.off('success');
        clipboard.destroy();
      });
      clipboard.on('error', () => {
        clipboard.off('error');
        clipboard.off('success');
        clipboard.destroy();
      });
      clipboard.onClick(event);
}

 
  • 用于剪切(????)

十四,在vue中使用节流与防抖?

节流与防抖是页面性能优化的常见方式

  • 防抖
    防抖是在规定时间内,多次执行一个方法,只有最后一次执行的方法生效。(在规定时间内重复执行时间重新计算)
    例如:点击按钮,发送请求。避免多次发送请求
新建debounce-throttle.js 在其中编写防抖代码
export const debounce = (fn, delay) => {
  let timer = null;    // timer 永驻于内存中
  return () => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn();
    }, delay); // 当鼠标停止2s后执行
  };
};

在组件中component.vue:

<el-button type="primary" @click="handleDebounce">debounce</el-button>
methods:
    handleDebounce: debounce(() => {
      getChart().then((res) => {
        console.log('res', res);
      });
    }, 2000),
  • 节流
    节流是在一段时间内多次触发一个方法,只执行一次这个方法。
    例如:鼠标移动发送请求,规定时间内只发送一次请求
debounce-throttle.js 在其中编写节流代码
export const throttle = (fn, interval) => {
  let startTime = new Date();
  let timer = null;
  return () => {
    const endTime = new Date();
    if (endTime - startTime < interval) {
      clearTimeout(timer);
      timer = setTimeout(() => { // 鼠标停止后执行一次
        startTime = new Date();
        fn();
      }, interval);
    } else {
      startTime = new Date();
      fn();
    }
  };
};
组件中:
<p style="width:200px;height:200px;border:1px solid #000" @mousemove="handleThrottle">鼠标移动加载</p>
    handleThrottle: throttle(() => {
      getChart().then((res) => {
        console.log('res', res);
      });
    }, 2000)

十五,关于深拷贝?

  • 简单的深拷贝(只能实现第一层的深拷贝,当是对象数组或者多维数组时则不能使用)
    es6 展开运算符
    数组的slice方法
    数组的concat()方法

  • 复杂的深拷贝
    Json.parse(Json.stringify())(当数组中含有undefined,function,symbol 会在转换过程中被忽略,所以只适用于一些简单的对象)

  • 最有效的方法是使用递归实现一个深拷贝方法:(但是此方法如果对象中的key值是数字,会导致对象中的顺序发生改变,需要改进)

  deepCopy(nums) {
      // 实现对象数组与多维数组的深拷贝
      const target = Array.isArray(nums) ? [] : {};
      for (const [key, value] of Object.entries(nums)) {
        if (typeof value === 'object') {
          target[key] = deepCopy(value);
        } else {
          target[key] = value;
        }
      }
      return target;
    }

十六: Vue中SortableJS 拖拽库的使用?

功能强大的JavaScript 拖拽库
npm 下载:

npm i -S sortablejs

引入到组件中:

import Sortable from 'sortablejs';

HTML:

<div id="serviceOrderFieldActiveTable">
    <table>
     <tbody>
        <tr><td>111</td></tr>
        <tr><td>222</td></tr>
        <tr><td>333</td></tr>
     </tbody>
    </table>
</div>

data:

sortable: null;

methods:

配置 Sortable:

const el = document.querySelectorAll('#serviceOrderFieldActiveTable table tbody')[0];
this.sortable = Sortable.create(el, {
  filter: '.filtered', // 过滤器,不需要进行拖动的元素
  preventOnFilter: true, // 在触发过滤器`filter`的时候调用`event.preventDefault()`
  ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
  setData: function(dataTransfer) {
    dataTransfer.setData('Text', '');
    // to avoid Firefox bug
    // Detail see : https://github.com/RubaXa/Sortable/issues/1012
  },
  onStart: (evt) => { // 开始拖拽的时候
    console.log('start', this.sortable.el.querySelector('.filtered')); // 有.filtered 的一行
    this.freezed = this.sortable.el.querySelector('.filtered');
  },
  onEnd: evt => { // 结束拖拽
    const list = evt.to; // 要拖拽的tbody
    const allTr = list.querySelectorAll('tr');
    const lastTr = allTr[allTr.length - 1]; // 最后一个tr
    console.log('oldindex', evt.oldIndex); // 拖拽前的索引
    console.log('newindex', evt.newIndex); // 拖拽后的索引
    const oldList = this.activeData.map(data => data.fieldId);
    console.log('oldList', oldList); // 数组中fieldId
    const targetRow = this.activeData.splice(evt.oldIndex, 1)[0]; // 拖拽的tr数据
    console.log('targetrow', targetRow);
    this.activeData.splice(evt.newIndex, 0, targetRow); // 将拖拽的数据插入到拖拽停止时的位置
    // for show the changes, you can delete in you code
    console.log('newlist', this.newList); // 数组元素的fieldId
    const tempIndex = this.newList.splice(evt.oldIndex, 1)[0]; // 取到拖拽前的fieldId
    this.newList.splice(evt.newIndex, 0, tempIndex); // 取到拖拽前的fieldId插入到拖拽后的位置
    console.log('afternewlist', this.newList); // 数组元素的fieldId
    }
    
})

十七. vue-multipane 的使用?

简介: 可改变大小的窗口 for vue.js

npm 下载:

npm i -S vue-multipane

在组件中使用:

<template>
      <multipane class="vertical-panes" layout="vertical">
          <div>Pane 1</div>
          <multipane-resizer></multipane-resizer>
          <div>Pane 2</div>
          <multipane-resizer></multipane-resizer>
          <div>Pane 3</div>
      </multipane>
</template>
<script>
import { Multipane, MultipaneResizer } from 'vue-multipane';
export default {
    components: {
        Multipane, 
        MultipaneResizer
    }
}
</script>
//修改样式:
<style>
    .vertical-panes {
        width: 100%;
    }
    .vertical-panes >>> .multipane-resizer {
        margin: 0;
        left: 0;
        position: relative;
    }
    .vertical-panes >>> .multipane-resizer:before {
      display: block;
      content: "";
      width: 2px;
      height: 800px;
      position: absolute;
      border-top: 1px solid #ccc;
      border-left: 1px solid #ccc;
      border-right: 1px solid #ccc;
    }
    .vertical-panes >>> .multipane-resizer:hover:before {
      border-color: #999;
    }
</style>

十八. Vue.config.productionTip = false

作用:阻止启动生产消息,常用作指令

没有Vue.config.productionTip = false这句代码,它会显示你生产模式的消息

开发环境下,Vue 会提供很多警告来帮你对付常见的错误与陷阱。 而在生产环境下,这些警告语句却没有用,反而会增加应用的体积。 此外,有些警告检查还有一些小的运行时开销,这在生产环境模式下是可以避免的。

如果不加Vue.config.productionTip = false 加上Vue.config.productionTip = false

十九. 判断图片地址是否有效?

 validateImg (path) {
      let ImgObj = new Image()
      ImgObj.src = path
      if (ImgObj.fileSize > 0 || (ImgObj.width > 0 && ImgObj.height > 0)) {
        return true
      } else {
        return false
      }
    }

二十. vue.config.js 中的配置

const CompressionPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']
const DEV_BACKEND_ADDR = process.env.VUE_APP_DEV_BACKEND_ADDR
module.exports = {
  publicPath: './', // 相对路径 ('./'),这样所有的资源都会被链接为相对路径,这样打出来的包可以被部署在任意路径
  outputDir: 'dist', // 输出文件目录,当运行 vue-cli-service build 时生成的生产环境构建文件的目录
  filenameHashing: false, // 设为 false 来关闭文件名哈希
  lintOnSave: true, // 是否在保存的时候使用 `eslint-loader` 进行检查
  assetsDir: 'static', // 放置public下生成静态资源的文件 (js、css、img、fonts)
  productionSourceMap: false, // 以减少打包后的项目体积
  chainWebpack: config => { // 便于阅读打包编译后的文件
    config.optimization.minimize(false)
  },
  configureWebpack: config => {
    config.plugins.push(
      new CompressionPlugin({ // 打包的时候开启gzip可以大大减少体积,非常适合于上线部署
        filename: '[path].gz[query]', // 目标资源文件名
        algorithm: 'gzip',
        test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'), // 匹配对应文件
        threshold: 10240, // 只处理比这个值大的资源。按字节计算。
        minRatio: 0.8, // 只有压缩率比这个值小的资源才会被处理
        cache: true // 是否缓存
      }))
    if (process.env.NODE_ENV === 'development') {
      config.devServer = {
        open: true, // 项目启动打开页面
        port: 24444, // 端口号配置
        watchOptions: {
          poll: false, // 通过传递 true 开启 polling,或者指定毫秒为单位进行轮询。
          aggregateTimeout: 300 // 当第一个文件更改,会在重新构建前增加延迟。这个选项允许 webpack 将这段时间内进行的任何其他更改都聚合到一次重新构建里。以毫秒为单位:
        },
        proxy: {
          '/api': {
            target: `${DEV_BACKEND_ADDR}`,
            ws: false, // ws:true / false,是否代理websockets
            changeOrigin: true, // 跨域时,本地就会虚拟一个服务器接收你的请求并代你发送该请求
            pathRewrite: {
              '^/api': ''
            }
          }
        }
      }
    }
    if (process.env.NODE_ENV === 'production') {
      config.devServer = {
        open: true, // 项目启动打开页面
        port: 2333, // 端口号配置
        watchOptions: {
          poll: false, // 通过传递 true 开启 polling,或者指定毫秒为单位进行轮询。
          aggregateTimeout: 300 // 当第一个文件更改,会在重新构建前增加延迟。这个选项允许 webpack 将这段时间内进行的任何其他更改都聚合到一次重新构建里。以毫秒为单位:
        },
        proxy: {
          '/api': {
            target: `${DEV_BACKEND_ADDR}`,
            ws: false, // ws:true / false,是否代理websockets
            changeOrigin: true, // 跨域时,本地就会虚拟一个服务器接收你的请求并代你发送该请求
            pathRewrite: {
              '^/api': ''
            }
          }
        }
      }
      config.optimization = { // 将打包后的依赖包分解, 将每个依赖包分解成单独的js文件,防止打包后依赖包体积过大
        runtimeChunk: 'single',
        splitChunks: {
          chunks: 'all',
          maxInitialRequests: Infinity,
          minSize: 20000, // 依赖包超过20000bit将被单独打包
          cacheGroups: {
            vendor: {
              test: /[\\/]node_modules[\\/]/,
              name (module) {
                // get the name. E.g. node_modules/packageName/not/this/part.js
                // or node_modules/packageName
                const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]
                // npm package names are URL-safe, but some servers don't like @ symbols
                return `npm.${packageName.replace('@', '')}`
              }
            }
          }
        }
      }

    }
  }
}