Vue开发中常见问题记录

1、vue-router 中使用 vuex

首先确保 main.js 中组合了 vuex 和 router

Vue.use(VueRouter)
Vue.use(Vuex)

new Vue({
  el: '#app',
  router: createVueRouter(VueRouter),
  store: createVueStore(Vuex),
  render: h => h(App)
})
复制代码

router.js 中可以直接使用 router.app.$options.store

let be4Each = function(router) {
  router.beforeEach((to, from, next) => {
    router.app.$options.store.dispatch('handleDoSomething').then((i) => {
      // doSomething
      next();
    });
  });
}

let createVueRouter = function(VueRouter) {
  let router = new VueRouter(routerConfig)
  be4Each(router)
  afterEach(router)
  return router
}

export default createVueRouter;
复制代码

2、vue组件中js定义的本地图片在页面无法显示

本地图片直接放到img标签中是可以展示的;但是通过js引入到img标签中,是无法展示的,需要通过require才能展示 assets文件夹下的图片photo.png 本地图片直接在img标签中使用

//图片可以显示
<img  src="@/assets/photo.png"  /> 
复制代码

js中的本地图片需要使用require才能显示

//图片不能显示
<img  :src="pic"  /> 
//图片可以显示
<img  :src="img"  /> 
复制代码
data( ){
    return {
        pic:'@/assets/photo.png',
        img:require('@/assets/photo.png')
    }
}
复制代码

3、vue build 过程取消 console、debugger 控制台信息输出

一、前言

Vue项目开发过程中,会经常需要使用console.log、console.info、alert等测试语句来输出内容,而在生产环境之中,我们不希望控制台同样输出以上信息,特别是用户信息相关。

打包前,逐一去删除、注释显然费时费力,好在Vue提供了通过配置文件修改配置变量,实现在开发环境打印,而生产环境不打印控制台信息的方法。

二、配置文件修改

修改build/webpack.prod.conf.js配置文件,找到UglifyJsPlugin配置,在compress中添加如下代码即可。

new UglifyJsPlugin({
  uglifyOptions: {
    compress: {
      warnings: false,
      // 打包的时候移除console、debugger
      drop_debugger: true, // 移除debugger
      drop_console: true, // 移除console
      pure_funcs: ['console.log','console.info']
    }
  },
  sourceMap: config.build.productionSourceMap,
  parallel: true
}),
复制代码

优化配置方式如下:

new UglifyJsPlugin({
  uglifyOptions: {
    compress: {
      warnings: false,
      // 打包的时候移除console、debugger
      drop_debugger: process.env.NODE_ENV=== 'production', // 移除debugger
      drop_console: process.env.NODE_ENV=== 'production', // 移除console
      warnings: process.env.NODE_ENV=== 'production', // 移除告警信息
      pure_funcs: ['console.log','console.info']
    }
  },
  sourceMap: config.build.productionSourceMap,
  parallel: true
}),
复制代码

其中,process.env.NODE_ENV定义在prod.env.js文件中,

module.exports = {
    NODE_ENV: "production"
}
复制代码

prod.env.js文件在config/index.jsbuild.env配置中引入。

build: {
    env: require('./prod.env')
}
复制代码

4、vue-cli 脚手架 webpack.base.conf.js 配置文件讲解

一、前言

webpack.base.conf.js 文件是vue开发环境和生产环境wepack相关配置文件,主要用来处理各种文件的配置。

// 引入nodejs路径模块
var path = require('path')
// 引入utils工具模块,utils主要用来处理css-loader和vue-style-loader的
var utils = require('./utils')
// 引入config目录下的index.js配置文件,主要用来定义一些开发和生产环境的属性
var config = require('../config')
// vue-loader.conf配置文件是用来解决各种css文件的,定义了诸如css,less,sass之类的和样式有关的loader
var vueLoaderConfig = require('./vue-loader.conf')
// 此函数是用来返回当前目录的平行目录的路径,因为有个'..'
function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: {
    // 入口文件是src目录下的main.js
    app: './src/main.js'
  },
  output: {
    // 路径是config目录下的index.js中的build配置中的assetsRoot,也就是dist目录
    path: config.build.assetsRoot,
    // 文件名称这里使用默认的name也就是main
    filename: '[name].js',
    // 上线地址,也就是真正的文件引用路径,如果是production生产环境,其实这里都是 '/'
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    // resolve是webpack的内置选项,顾名思义,决定要做的事情,也就是说当使用 import "jquery",该如何去执行这件事
    // 情就是resolve配置项要做的,import jQuery from "./additional/dist/js/jquery" 这样会很麻烦,可以起个别名简化操作
    extensions: ['.js', '.vue', '.json'], // 省略扩展名,也就是说.js,.vue,.json文件导入可以省略后缀名,这会覆盖默认的配置,所以要省略扩展名在这里一定要写上
    alias: {
      //后面的$符号指精确匹配,也就是说只能使用 import vuejs from "vue" 这样的方式导入vue.esm.js文件,不能在后面跟上 vue/vue.js
      'vue$': 'vue/dist/vue.esm.js',
      // resolve('src') 其实在这里就是项目根目录中的src目录,使用 import somejs from "@/some.js" 就可以导入指定文件,是不是很高大上
      '@': resolve('src')
    }
  },
  // module用来解析不同的模块
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        // 也就是说,对.js和.vue文件在编译之前进行检测,检查有没有语法错误
        loader: 'eslint-loader',
        // 此选项指定enforce: 'pre'选项可以确保,eslint插件能够在编译之前检测,如果不添加此项,就要把这个配置项放到末尾,确保第一个执行
        enforce: 'pre',
        // include选项指明这些目录下的文件要被eslint-loader检测,还有一个exclude表示排除某些文件夹
        include: [resolve('src'), resolve('test')],
        // options表示传递给eslint-loader的参数
        options: {
          // formatter是参数的名称,eslint-friendly-formatter是eslint的一个报告总结插件,也就是说eslint的检测报告非常难看懂,这个插件就是整理这些报告方便查阅的
          formatter: require('eslint-friendly-formatter')
        }
      },
      {
        test: /\.vue$/,
        // 对vue文件使用vue-loader,该loader是vue单文件组件的实现核心,专门用来解析.vue文件的
        loader: 'vue-loader',
        // 将vueLoaderConfig当做参数传递给vue-loader,就可以解析文件中的css相关文件
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        // 对js文件使用babel-loader转码,该插件是用来解析es6等代码
        loader: 'babel-loader',
        // 指明src和test目录下的js文件要使用该loader
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        // 对图片相关的文件使用 url-loader 插件,这个插件的作用是将一个足够小的文件生成一个64位的DataURL
        // 可能有些老铁还不知道 DataURL 是啥,当一个图片足够小,为了避免单独请求可以把图片的二进制代码变成64位的
        // DataURL,使用src加载,也就是把图片当成一串代码,避免请求,神不神奇??
        loader: 'url-loader',
        options: {
          // 限制 10000 个字节一下的图片才使用DataURL
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]') // 这个函数执行结果是 /img/[name].[hash:7].[ext]
          // 不知道吧 name 设置成 /img/[name].[hash:7].[ext] 意欲何为,猜测应该是输出图片的路径或者是解析图片的路径
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        // 字体文件处理,和上面一样
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}
复制代码

二、注

对于文件小于10000byte,在生成的代码用中base64来替代; 大于10000byte,按[name].[hash:7].[ext]的命名方式放到static/img下面,方便做cache; 因为项目中会有动态引入而无法提前通过loader加载的图片,用CopyWebpackPlugin放到dist目录下。所以最后build完的图片资源就是两部分:一部分是dev下的整个图片文件夹(被复制了一份),另外就是经过url-loader处理过的dist/img下带hash的图片。

5、<keep-alive> 实现页面缓存

一、引入场景

官网解释:<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。 当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。 在 2.2.0 及其更高版本中,activated 和 deactivated 将会在 <keep-alive> 组件内的所有嵌套组件中触发。 主要用于保留组件状态或避免重新渲染

二、应用场景

如果未使用keep-alive组件,则在页面回退时仍然会重新渲染页面,触发created钩子,用户体验不好。 在菜单存在多级关系,多见于列表页+详情页的场景,使用keep-alive组件会显著提高用户体验,如:商品列表页点击商品跳转到商品详情,返回后仍显示原有信息,订单列表跳转到订单详情,返回,等等场景。

三、keep-alive 生命周期

初次进入时:created > mounted > activated;退出后触发 deactivated;

再次进入:会触发 activated;事件挂载的方法等,只执行一次的放在 mounted 中;组件每次进去执行的方法放在 activated 中;

四、项目实践

1.更改App.vue

<div id="app" class='wrapper'>
    <keep-alive>
        <!-- 需要缓存的视图组件 --> 
        <router-view v-if="$route.meta.keepAlive"></router-view>
     </keep-alive>
      <!-- 不需要缓存的视图组件 -->
     <router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
复制代码

2.在路由中设置keepAlive

{
  path: 'list',
  name: 'itemList', // 商品管理
  component (resolve) {
    require(['@/pages/item/list'], resolve)
 },
 meta: {
  keepAlive: true,
  title: '商品管理'
 }
}
复制代码

3.更改 beforeEach钩子

这一步是为了清空无用的页面缓存。 假设现在A、B两个页面都开启的缓存:

若第一次进入A页面后退出,再次进入页面时,页面不会刷新。这和目前的业务逻辑不符。我们想要的结果是A页面前进后返回,页面保持不变,而不是退出后重新进入保持不变。

在进入过A页面后进入B页面,经过测试后发现,B页面竟然会显示A页面的缓存,尽管url已经改变。

为了解决这个问题,需要判断页面是在前进还是后退。 在beforeEach钩子添加代码:

let toDepth = to.path.split('/').length
let fromDepth = from.path.split('/').length
if (toDepth < fromDepth) {
  // console.log('back...')
  from.meta.keepAlive = false
  to.meta.keepAlive = true
}
复制代码

五、记录页面滚动位置

keep-alive并不会记录页面滚动位置,如需在跳转时需要记录当前的滚动位置,可在触发activated钩子时重新定位到原有位置。 具体设计思路:

deactivated钩子中记录当前滚动位置,使用localStorage

deactivated () {
 window.localStorage.setItem(this.key, JSON.stringify({
 listScrollTop: this.scrollTop
 }))
}
复制代码

activated钩子中滚动:

this.cacheData = window.localStorage.getItem(this.key) ?JSON.parse(window.localStorage.getItem(this.key)) : null
$('.sidebar-item').scrollTop(this.cacheData.listScrollTop)
复制代码

6、ES数组操作:splice() 实现数组删除、替换、增加指定元素

一、前言

注:该方法会改变原始数组。

ES6数组中删除指定元素

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。

arr.splice(arr.findIndex(item => item.id === data.id), 1)
复制代码

二、splice实现删除、替换、新增

splice(index,len,[item]) 
复制代码

splice有3个参数,它也可以用来替换/删除/添加数组内某一个或者几个值。

index:数组开始下标 len: 替换/删除的长度 item:替换的值,删除操作的话, item为空 如:

arr = ['a','b','c','d']
复制代码

删除 ---- item不设置

arr.splice(1,1) //['a','c','d'] //删除起始下标为1,长度为1的一个值,len设置的1,如果为0,则数组不变
arr.splice(1,2) //['a','d'] //删除起始下标为1,长度为2的一个值,len设置的2
复制代码

替换 ---- item为替换值

arr.splice(1,1,'ttt') //['a','ttt','c','d'] //替换起始下标为1,长度为1的一个值为'ttt',len设置的1
arr.splice(1,2,'ttt') //['a','ttt','d'] //替换起始下标为1,长度为2的两个值为'ttt',len设置的1
复制代码

添加 ---- len设置为0,item为添加值

arr.splice(1,0,'ttt') //['a','ttt','b','c','d'] //表示在下标为1处添加一项'ttt'
复制代码

7、vue实战——vue中发送AJAX请求

一、简介

1)vue本身不支持发送AJAX请求,需要使用vue-resource、axios等插件实现。

2) axios是一个基于Promise的HTTP请求客户端,用来发送请求,也是vue2.0官方推荐的,同时不再对vue-resource进行更新和维护。 参考:GitHub上搜索axios,查看API文档

二、使用axios发送AJAX请求 1、安装axios并引入

1)npm的方式: $ npm install axios -S

2)bower的方式:$ bower install axios

3)cdn的方式:

2、基本用法 1)axios([options])

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>axios发送ajax请求基本用法</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        <button @click="send">发送ajax请求</button>
    </div>
    <script>
        new Vue({
            el:"#app",
            methods:{
                send(){
                    axios({
                       method:'get',
                        url:'user.json'
                    }).then(function(res){
                        console.log(res.data.name);
                    });
                }
            }
        });
    </script>
</body>
</html>
复制代码

2)axios.get(url[,options]);

传参方式:
(1)通过url传参axios(‘url?key=value&key1=val2’).then();
(2)通过params选项传参 axios(‘url’,{params:{key:value}}).then();\

3)axios.post(url,data,[options]);

axios默认发送数据时,数据格式是Request Payload,并非常用的Form Data格式, 所以参数必须要以键值对形式传递,不能以json形式传。

传参方式:
(1)自己拼接为键值对 axios.post(‘url’,‘key=value&key1=value1’).then();
(2)使用transformRequest,在请求发送前将请求数据进行转换

axios.post('url',data,{
    transformRequest:[
            function(data){
                let params = '';
                for(let index in data){
                    params +=index+'='+data[index]+'&';
                }
                return params;
            }
    ]
}).then(function(res){
    console.log(res.data)
});
复制代码

3)如果使用模块化开发,可以使用qs模块进行转换

axios本身并不支持发送跨域的请求,没有提供相应的API,作者也暂没计划在axios添加支持发送跨域请求, 所以只能使用第三方库

三、跨域请求(使用vue-resource发送跨域请求) 1、使用vue-resource发送跨域请求步骤

安装vue-resource并引入:

npm install vue-resource -S
复制代码

基本用法:

使用this.$http.jsonp(url,[ops]) 发送请求

2、基本使用演示(360搜索)
1)打开360搜索,然后输入字符’a’会有一些搜索选项自动提示,如图

2)复制链接
sug.so.360.cn/suggest?cal…
3)代码演示

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>使用vue-resource发送跨域请求</title>
    <!--引入vue、vue-resource文件-->
    <script src="vue.min.js"></script>
    <script src="vue-resource.min.js"></script>
</head>
<body>
    <div id="app">
        <button @click="sendJsonp">send</button>
    </div>
    <script>
            var vm = new Vue({
                el:"#app",
                methods:{
                    sendJsonp:function(){
                        this.$http.jsonp('https://sug.so.360.cn/suggest',{
                            params:{
                                word:'a'
                            }
                        }).then(function(res){
                            console.log(res.data);
                        });
                    }
                }
            });
    </script>
</body>
</html>
复制代码

4)结果\

3、基本例子演示(百度搜索)

1)要求同360搜索的要求

2)复制链接 =1526436420943”>sp0.baidu.com/5a1Fazu8AA5…

3)代码演示

之前360搜索jsonp回调的参数名是callback,而百度使用的参数名为cb,所以会报错,新增 jsonp:'cb'

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>使用vue-resource发送跨域请求</title>
    <!--引入vue、vue-resource文件-->
    <script src="vue.min.js"></script>
    <script src="vue-resource.min.js"></script>
</head>
<body>
    <div id="app">
        <button @click="sendJsonp">send</button>
    </div>
    <script>
            var vm = new Vue({
                el:"#app",
                methods:{
                    sendJsonp:function(){
         this.$http.jsonp('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su',{
                            params:{
                                wd:'a'
                            },
                            jsonp:'cb'
                        }).then(function(res){
                            console.log(res.data);
                        });
                    }
                }
            });
    </script>
</body>
</html>
复制代码

4)结果

8、vue cryptoJS AES加密

1.先在vue项目中安装crypto-js

npm install crypto-js
复制代码

2.新建一个secret.js文件

//引用AES源码js
const CryptoJS = require('crypto-js');

const key = CryptoJS.enc.Utf8.parse("1234123412ABCDEF");//十六位十六进制数作为密钥
const iv = CryptoJS.enc.Utf8.parse('ABCDEF1234123412');//十六位十六进制数作为密钥偏移量

//解密方法
function Decrypt(word) {
   //先将Base64还原一下,因为加密的时候做了一些字符的替换
    const restoreBase64 = word.replace(/\-g/,'+').replace(/_/,'/');

    //返回的是解密后的对象
    let decrypt = CryptoJS.AES.decrypt(restoreBase64,key,{
        iv:iv,
        mode:CryptoJS.mode.CBC,
        padding:CryptoJS.pad.Pkcs7
    });

    //将解密对象转换成UTF8的字符串
    let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
    //返回解密结果
    return decryptedStr.toString();
}

//加密方法
function Encrypt(word){
    console.log('组件里的:',word);
    let srcs = CryptoJS.enc.Utf8.parse(word);
     //CipherOption,加密的一些选项:
    //mode:加密模式,可取值(CBC,CFB,CTR,CTRGladman,OFB,ECB),都在CryptoJS.mode对象下
    //padding:填充方式,可取值(Pkcs7,Ansix923,Iso10126,ZeroPadding,NoPadding),都在CryptoJS.pad对象下
    //iv:偏移量,mode===ECB时,不需要iv
    //返回的是一个加密对象
    let encrypted = CryptoJS.AES.encrypt(srcs,key,{
        iv:iv,
        mode:CryptoJS.mode.CBC,
        padding:CryptoJS.pad.Pkcs7
    });
    //将结果进行base64加密
    return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}

// export {Decrypt,Encrypt}
export {Encrypt}
复制代码

3.vue页面内容

import {Encrypt} from "../../utils/secret";
var userName = Encrypt(this.userName)//加密用户名
var userPassword = Encrypt(this.password)//加密用户密码
console.log('加密后:',userName)

console.log('加密后:',userPassword)
复制代码

9、vue axios.post请求后端接收不到参数问题

处理方案一:请求拦截

// post请求后台无法获取传递参数
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';//配置请求头
axios.defaults.transformRequest = function (data) {
  return qs.stringify(data, {arrayFormat: 'brackets'})
}
复制代码

处理方案二:

//axios
axios({
	method: "post",
	url: host + '/xpg/baoxian/getSpbxList',
	data: urlstringify({
		"fl": title.trim(),
		"page": 1,
 		"size": 10
	}),
	headers: {
		"Content-Type": 'application/x-www-form-urlencoded'
	}
})
.then(function (response) {

}
.catch(function (error) {
	console.log(error);
});

//转义方法
function urlstringify(obj) {//字符串序列化
    var str = '';
    for (let key in obj) {
        if (Object.prototype.toString.call(obj[key]) === '[object Array]' || obj[key].constructor === Object) {
            //数组,对象
            for (var arrKey in obj[key]) {
                if (Object.prototype.toString.call(obj[key][arrKey]) === '[object Array]' || obj[key][arrKey].constructor === Object) {
                    //数组,对象
                    for (var arrarrKey in obj[key][arrKey]) {
                        str += '&' + key + '[' + arrKey + '][' + arrarrKey + ']=' + obj[key][arrKey][arrarrKey];
                    }
                } else {
                    //普通
                    str += '&' + key + '[' + arrKey + ']=' + obj[key][arrKey];
                }
            }
        } else {
            //普通
            str += '&' + key + '=' + obj[key];
        }
    }
    return str.substring(1);
}
复制代码

11、vue中如何实现中英或多语言切换(含element-ui)

1、首先使用cmd窗口安装 vue-i18n 包

npm install vue-i18n --save-dev
复制代码

2、然后在main.js中引入

import VueI18n  from 'vue-i18n' //导入包
//导入locales js
import i18n from './common/locales/index.js'

new Vue({
  i18n,
  el: '#app',
  render: h => h(App)
})
复制代码

3、index.js en.js内容:

4、locales js:

import Vue from 'vue' // 引入vue实例
import VueI18n from 'vue-i18n' // 引入vue-i18n多语言包

// element ui国际化
import ElementUI from 'element-ui'
import enLocale from 'element-ui/lib/locale/lang/en'
import twLocale from 'element-ui/lib/locale/lang/zh-tw'
import idLocale from 'element-ui/lib/locale/lang/en'
import jpLocale from 'element-ui/lib/locale/lang/en'
// import idLocale from 'element-ui/lib/locale/lang/id-ID'
import ElementUILocale from 'element-ui/lib/locale'

Vue.use(VueI18n) // vue使用vue-i18n

//element ui国际化
Vue.use(ElementUI,{ElementUILocale})

const DEFAULT_LANG = 'en' // 默认语言为英文
const LOCALE_KEY = 'localeOld_block_chain' // localStorage来存放的key,名字随便定,接下来会用到。

const locales = { // 引入zh.js以及en.js
  tw: require('../i18n/zh-tw.js'),
  en: require('../i18n/en.js'),
  id: require('../i18n/id-ID.js'),
  jp: require('../i18n/jp-JP.js')
}

const i18n = new VueI18n({ // 创建带有选项的 VueI18n 实例
  locale: DEFAULT_LANG, // 语言标识,在这里默认为en,即为英文
  messages: locales // 语言包,上边创建的json文件
})

// element ui国际化
const UIlocales = {
  tw: twLocale,
  en: enLocale,
  id: idLocale,
  jp: jpLocale,
}
const setUIlocales = lang => {
  switch (lang) {
    case 'tw':
      return UIlocales.tw
    case 'en':
      return UIlocales.en
    case 'id':
      return UIlocales.id
    case 'jp':
      return UIlocales.jp
  }
}

export const setup = lang => { //切换语言的函数,lang为语言标识,en或者tw
  // 在此判断lang的值,如果未定义,则让lang默认为DEFAULT_LANG,目的是为了让用户在未选择语言的时候默认为英文。
  if (lang == undefined) {
    lang = window.localStorage.getItem(LOCALE_KEY)
    if (locales[lang] == undefined) {
      lang = DEFAULT_LANG
    }
  } // 若lang有值,那么存入localStorage中,key为LOCALE_KEY,value为lang。
  window.localStorage.setItem(LOCALE_KEY, lang)
  Object.keys(locales).forEach(item => {
    document.body.classList.remove('lang-${item}')
  })
  document.body.classList.add('lang-${lang}')
  document.body.setAttribute('lang', lang)

  Vue.config.lang = lang
  i18n.locale = lang
  // element ui 切换语言
  ElementUILocale.use(setUIlocales(lang))

  // 挂载需要用到多语言的js文件
  const vueInstance = new Vue({ i18n });
  if(vueInstance&&vueInstance.$i18n&&vueInstance.$i18n.messages){
    // console.log("Vue",vueInstance)
    let tipsJs = vueInstance.$i18n.messages[lang];
    // console.log("tipsJs",tipsJs.lang.tipsJs)
    Vue.prototype.$tipsJs = tipsJs.lang.tipsJs;
  }
}

setup()
export default i18n
复制代码

5、页面使用多语言 js引入this.$t('') template使用{{i18n.Markets}}

<div class="container_nav">
  <p class="tac white fs16">{{i18n.Markets}}</p>
</div>
复制代码
computed: {
  i18n() {
    return this.$t('lang.markets')
  },
},
复制代码

6、自定义js引入i18n

// 引入多语言
import i18n from '../common/locales/index.js';
let envIndex = i18n.t('lang').envIndex;
复制代码

13、详解vue 路由跳转四种方式 (带参数)

1.  router-link

  1. 不带参数
<router-link :to="{name:'home'}"> 
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name 
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
复制代码

2.带参数(建议query传参数,不用配置路由)

<router-link :to="{name:'home', params: {id:1}}"> 
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id" 
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id

<router-link :to="{name:'home', query: {id:1}}"> 
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id
复制代码

2.  this.$router.push() (函数里面调用)

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 会消失

复制代码

3.  this.$router.replace() (用法同上,push)

4.  this.$router.go(n)

this.$router.go(n)

向前或者向后跳转n个页面,n可为正整数或负整数

ps : 区别

this.$router.push

跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面

this.$router.replace

跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)

this.$router.go(n)

向前或者向后跳转n个页面,n可为正整数或负整数

14、vue前进刷新,后退不刷新and按需刷新

1.在路由文件router.js中为目标列表页设置meta参数,里面包含keepAlive和ifDoFresh字段

{
    path:'*',
    name:'datalist',
    component: resolve => require(['@/view/datalist'], resolve),
    meta:{
        keepAlive: true,
        ifDoFresh:false
    }
},
复制代码

2.在程序主入口main.vue中设置页面根据keepAlive字段判断是否使用keep-alive组件。

<div class="main">
    <keep-alive>
       <router-view v-if="$route.meta.keepAlive"/>
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive"/>
</div>
复制代码

3.在目标列表页的beforeRouteEnter方法中判断页面进入方式(详情页,编辑页或其他方式),根据需求将路由参数的ifDoFresh字段设为true/false,

在页面的activated(开启了 keepAlive: true的页面在第二次进入时是无法触发mounted发法的)方法中根据ifDoFresh字段判断是否刷新页面

beforeRouteEnter (to, from, next) {
    if(from.name!='详情页'&&from.name!='编辑页'){ 
        to.meta.ifDoFresh = true; 
    } 
    next(); 
}, 
activated(){ 
      //开启了keepAlive:true后再次进入,以前的搜索条件和页数都不会变,无论什么情况都调用一下获取数据的接口,这样就能得到当前搜索条件和页数的最新数据
      if(this.$route.meta.ifDoFresh){
        //重置ifDoFresh
        this.$route.meta.ifDoFresh = false;
         //获取列表数据方法第一参数为是否重置搜索条件和页数 this.getData(true);
    }else{
        this.getData();
    }
} 
复制代码

15、vue项目添加发布路径

项目模板

我的项目是使用vue init webpack创建的,所以我需要修改3个文件。

修改文件

(1)index.html

head中添加<meta base="/macth/">

(2)路由文件添加base:'/match/'

let router = new Router({
  base:'/match/,
  routes: [
      {
          path: '/login',
          type: 'login',
          component: Login,
          meta:{
              keepAlive: true,
              ifDoFresh:true,
          }
        },
  ]
});
复制代码

(3)配置文件/config/index.js 修改assetsPublicPath参数

assetsPublicPath: '/macth/',
复制代码

总结:打包修改assetsPublicPath也可满足,后台上传不同地址即可。

16、 es6新增语法之${}

1、反单引号怎么打出来?
将输入法调整为英文输入法,单击键盘上数字键1左边的按键。

2、用法
step1: 定义需要拼接进去的字符串变量
step2: 将字符串变量用${}包起来,再写到需要拼接的地方

3、示例代码:

let a='Karry Wang';

let str=`I love ${a}, because he is handsome.`;
//注意:这行代码是用返单号引起来的

alert(str);
复制代码

一定是用反单引号啊!不要写成单引号了!!

17、Form 表单重置失败问题解决

一、前言 在Vue项目开发过程中,应用this.refs[name].resetFields();实现表单搜索元素重置时发现失效。经检查发现是form−item绑定的属性prop与包裹元素el−input绑定值不一致造成的。现梳理有关应用this.refs[name].resetFields();实现表单搜索元素重置时发现失效。经检查发现是form-item绑定的属性prop与包裹元素el-input绑定值不一致造成的。现梳理有关应用this.refs[name].resetFields();实现表单搜索元素重置时发现失效。经检查发现是form−item绑定的属性prop与包裹元素el−input绑定值不一致造成的。现梳理有关应用this.refs[name].resetFields();重置表单注意事项。

Form必须要有ref属性;
Form必须绑定:model;

<Form ref="submitUser" :model="submitUser">
复制代码
  • FormFormItem中必须要有prop属性;
<FormItem prop="realName">
</FormItem>
复制代码
  • Form包裹的元素绑定值需与FormItemprop属性名称保持一致(可类比Form表单校验规则)
<Form ref="submitUser" :model="submitUser">
	<FormItem prop="uname">
		<Input type="text" v-model="submitUser.uname" placeholder="用户名"></Input>
	</FormItem>
	<FormItem prop="passwd">
		<Input type="text" v-model="submitUser.passwd" placeholder="密码"></Input>
	</FormItem>
	<FormItem>
		<Button type="info" @click="query">Login</Button>
		<Button @click="handleReset('submitUser')" style="margin-left: 8px;" type="info">Reset</Button>
	</FormItem>
</Form>
<script>
    export default {
    	data () {
            return {
				submitUser:{
					realName:'',
					identityNumber:'',
					mobileNumber:'',
					telePhoneNumber:''
				}
			}
        },
        methods:{
			handleReset (name) {
				this.$refs[name].resetFields();
			}
		}
    }
</script>
复制代码

18、Vue监听数据变化

监听数据变化,在Vue中是通过侦听器来实现的,时刻监听某个数据的变化

watch的基本用法

  1. 侦听器的书写位置
    写在export default中与data和methods用,隔开
<script>
  export default {
    name: "app",
    // 数据 key---data value---Function
    data: function () {
      return {
         count: 1
      };
    },
    // 方法 key---methods value---{}
    methods: {},
    //在export default中添加即可不用管顺序
    watch: {
       //监听内容
       count() {
        console.log("count发生了变化");
      }
    }
  };
</script>
复制代码

监听器里的方法一定要与被监听的变量名一致

侦听器的进阶用法

获取前一次的值

有的时候需要上一次的数据,再上一个案例中添加一个参数即可获取旧值

watch:{
    inputValue(value,oldValue) {
        // 第一个参数为新值,第二个参数为旧值,不能调换顺序
        console.log(`新值:${value}`);
        console.log(`旧值:${oldValue}`);
    }
}
复制代码

handler方法immediate属性

immediate: 可以让页面第一次渲染的时候去触发侦听器
handler: 监听到修改之后这个函数会执行
侦听器实际上是一个对象,里面包含了handler方法和其他属性:

<script>
  export default {
    name: "app",
    watch: {
      firstName: {
        handler: function (newName, oldName) {
          this.fullName = newName + " " + this.lastName;
        },
        immediate: true
      }
    }
  };
</script>
复制代码

19、vue中页面加载进度条效果

滚动条效果插件:nprogress

  • 安装:
cnpm install --save nprogress
复制代码
  • main.js中引入:
//引入nprogressimport NProgress from 'nprogress'import 'nprogress/nprogress.css' //这个样式必须引入
复制代码
  • main.js中进行一些配置:
NProgress.configure({     
    easing: 'ease',  // 动画方式    
    speed: 500,  // 递增进度条的速度    
    showSpinner: false, // 是否显示加载ico    
    trickleSpeed: 200, // 自动递增间隔    
    minimum: 0.3 // 初始化时的最小百分比
})
复制代码
  • 接下来,还是在mian.js中,
router.beforeEach((to, from , next) => { 
    // 每次切换页面时,调用进度条 
    NProgress.start(); 
    // 这个一定要加,没有next()页面不会跳转的。这部分还不清楚的去翻一下官网就明白了 
    next(); 
});
复制代码
router.afterEach(() => { 
    // 在即将进入新的页面组件前,关闭掉进度条 
    NProgress.done() 
})
复制代码

20、 webpack only one instance of babel-polyfill is allowe

旧的浏览器不兼容babel-polyfill

项目是用vue-cli2搭建的,主要引起错误的原因如下图所示:

用了这种方式后,babel-polyfill与其他的插件造成了冲突,也就是说有两个地方都用到了babel-polyfill

于是我的解决方案如下所示:

第一步去掉配置文件的babel-polyfill

entry: {
    app: './src/main.js',
}
复制代码

第二步,在main.js加上以下代码

if (!global._babelPolyfill) {
  require('babel-polyfill');
}
复制代码

21、nvm常用命令 切换node

常用命令:


nvm ls :列出所有已安装的 node 版本

nvm ls-remote :列出所有远程服务器的版本(官方node version list)

nvm list :列出所有已安装的 node 版本

nvm list available :显示所有可下载的版本

nvm install stable :安装最新版 node

nvm install [node版本号] :安装指定版本 node

nvm uninstall [node版本号] :删除已安装的指定版本

nvm use [node版本号] :切换到指定版本 node

nvm current :当前 node 版本

nvm alias [别名] [node版本号] :给不同的版本号添加别名

nvm unalias [别名] :删除已定义的别名

nvm alias default [node版本号] :设置默认版本

复制代码

22、前端脚手架并发布到npm

  1. 首先在本地创建一个npm项目。
  2. 在本地新建文件夹。vue-test
  3. 创建好文件夹后,使用cmd进入该创建好的文件夹。
  4. 使用npm init 全部Y 生成一个默认的package.json模板,在package.json 中需要增加一个bin配置,声明这个bin的名称以及对应文件地址。
  1. 在当前文件夹下新建一个文件夹目录bin目录。

  1. 在新建的这个bin目录下新建一个index.js文件。
  2. 编辑这个index.js 文件,在文件中添加 console.log("vue-test")
  3. 并且在当前文件的首开始,添加一行 #!/usr/bin/env node

  1. 将脚手架发布到npm(此处需要大家先行在npm进行注册。) 使用npm login 进行npm登陆。 然后使用 npm publish 发布 (此处报错,建议百度。一般都是名称重复问题)
  2. 推送成功后,在自己电脑本地则可以使用 npm install vue-test -g 进行安装
  3. 安装完成后,则可以使用 vue-test (package.json 中bin配置的那个名称)来进行验证

23、 vue 刷新不出现白屏的解决办法

1、路由切换操作

// 如果在这个空间有权限,刷新当前页面
this.$router.push(path).catch(err => err);
复制代码

2、在使用router-view 的地方(一般在APP.vue文件)

<template>
  <div class="home">
    <router-view class="right"  v-if="isRouterAlive" />
  </div>
</template>

<script>

export default {
  provide() {
    return {
      reload: this.reload
    }
  },
  data() {
    return {
      isRouterAlive: true
    };
  },
  mounted() {

  },
  methods: {
    reload() {
      this.isRouterAlive = false
      this.$nextTick(() => {
        this.isRouterAlive = true
      })
    }
  }
};
</script>
复制代码

3、在被刷新的地方

方式一:watch监听

export default {
    inject: ['reload'],
    watch: {
      // 监听到路由跳转时,强制刷新
      '$route': {
        handler() {
          this.reload()
        }
      }
    },
}
复制代码

方式二,methods方法调用刷新

export default {
    inject: ['reload'],
    created() {
      this.refresh();
    },
    methods: {
      //刷新方法
      refresh(){
          this.reload()
      }
    },
}
复制代码

24、 vue-resource和axios和vue-axios区别

vue-resource(已经淘汰)

目前主流的 Vue 项目,都选择 axios 来完成 ajax 请求,基于 Promise 的 HTTP 请求客户端,可同时在浏览器和 Node.js 中使用。

vue-axios 是在axios基础上扩展的插件,在Vue.prototype原型上扩展了$http等属性,可以更加方便的使用axios,使用vue-axios更多是为了符合规范,并且方便协作吧。

总结:

25、window.open在Safari中不能打开的问题

1.原因分析:

原因:大部分现代的浏览器(Chrome/Firefox/IE 10+/Safari)都默认开启了阻止弹出窗口的策略,原因是window.open被广告商滥用,严重影响用户的使用。这个阻止弹出窗口的操作,并不是直接封杀window.open(),而是会根据用户的行为来判断这次window.open()是否属于流氓操作。
如果是由用户触发的动作所引起的 window.open 就不会被浏览器所阻止,比如写在 onclick 这些事件 handler 里的,但如果是代码自己触发的就会被阻止。
那么,我们可以知道,在Safari中无法open新窗口,原因是Safari的安全机制将其阻挡。
并不是所有地方都无法正常使用,在一些ajax或者jquery的getjson等回调代码中只要调用window.open都失效。原因是苹果的安全策略拦截。

2.解决方案:

解决办法有4种:

(1)用window.location.replace()来替代,【或者改变location.href,可以解决,缺点就是不是新开的窗口】

(2)苹果系统设置,偏好设置->安全性,去掉阻止弹窗的复选框就ok了。 【不建议,会改变用户的设置】

(3)在回到函数中生成一个链接,让用户再次点击下,因为链接是无论如何不会被拦截的。【不建议,多加了一个动作】

(4)在回调代码之前打开一个空窗口,例如 var w=window.open(xxx); 然后在回调函数中设置它的location。【推荐】

例如w.location='juejin.cn/user/229183…'; 具体分析和代码参考:

var openWin = function(){
    var winRef = window.open("url","_blank");
    $.ajax({
        type: '',
        url: '',
        data: '',
        ......
        success:function(json){
            winRef.location = "新的url";
        }
    });
};
复制代码

26、解决Vuex-在F5刷新页面后数据不见

1.问题场景

vuex优势:相比sessionStorage,存储数据更安全,sessionStorage可以在控制台被看到。 vuex劣势:在F5刷新页面后,vuex会重新更新state,所以,存储的数据会丢失。 为了克服这个问题, vuex-persistedstate出现了

2.原理

  • 将vuex的state存在localStorage或sessionStorage或cookie中一份
  • 刷新页面的一瞬间,vuex数据消失,vuex会去sessionStorage中拿回数据,变相的实现了数据刷新不丢失~

3.使用方法

  • 安装
npm install vuex-persistedstate  --save
复制代码
  • 在store下的index.js中,引入并配置

    import createPersistedState from "vuex-persistedstate"
    
    const store = new Vuex.Store({
      // ...
      plugins: [createPersistedState()]
    })
    复制代码
  • 此时可以选择数据存储的位置,可以是localStorage/sessionStorage/cookie,此处以存储到sessionStorage为例,配置如下:

    import createPersistedState from "vuex-persistedstate"
    const store = new Vuex.Store({
      // ...
      plugins: [createPersistedState({
          storage: window.sessionStorage
      })]
    })
    复制代码

4. 存储指定state:

vuex-persistedstate默认持久化所有state,指定需要持久化的state,配置如下:

import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
  // ...
  plugins: [createPersistedState({
      storage: window.sessionStorage,
      reducer(val) {
          return {
          // 只储存state中的user
          user: val.user
        }
     }
  })]
复制代码

5. 如果此刻想配置多个选项,将plugins写成一个一维数组,不然会报错。

import createPersistedState from "vuex-persistedstate"
import createLogger from 'vuex/dist/logger'
// 判断环境 vuex提示生产环境中不使用
const debug = process.env.NODE_ENV !== 'production'
const createPersisted = createPersistedState({
  storage: window.sessionStorage
})
export default new Vuex.Store({
 // ...
  plugins: debug ? [createLogger(), createPersisted] : [createPersisted]
})
复制代码

27、vue实现滚动公告栏效果组件

<template>
  <div>
    <div class="textBox">
      <transition name="slide">
        <p class="text" :key="text.id">
        <el-tag type="warning">{{text.val.tag}}</el-tag>
            {{text.val.title}}</p>
      </transition>
    </div>
  </div>
</template>

<script>
export default {
  name: 'scroll',
  data () {
    return {
      textArr: [
        {tag:'精彩推荐',title:'第1条公告'},
         {tag:'热门推荐',title:'第3条公告'},
        {tag:'精彩推荐',title:'第3条公告'},
        {tag:'公司公告',title:'第4条公告'},
        {tag:'热门推荐',title:'第5条公告'},
      ],
      number: 0
    }
  },
  computed: {
    text () {
      return {
        id: this.number,
        val: this.textArr[this.number]
      }
    }
  },
  mounted () {
    this.startMove()
  },
  methods: {
    startMove () {
      // eslint-disable-next-line
      let timer = setTimeout(() => {
        if (this.number === this.textArr.length) {
          this.number = 0;
        } else {
          this.number += 1;
        }
        this.startMove();
      }, 2500); // 滚动不需要停顿则将2000改成动画持续时间
    }
  }
}
</script>

<style scoped>
  .textBox {
    width: 100%;
    height: 40px;
    margin: 0 auto;
    overflow: hidden;
    position: relative;
    text-align: center;
  }
  .text {
    width: 100%;
    position: absolute;
    bottom: 0;
  }
  .slide-enter-active, .slide-leave-active {
    transition: all 0.5s linear;
  }
  .slide-enter{
    transform: translateY(20px) scale(1);
    opacity: 1;
  }
  .slide-leave-to {
    transform: translateY(-20px) scale(0.8);
    opacity: 0;
  }
</style>
复制代码

28、解决input只能输入金额类型的方案(金额输入框只能输入2位小数)

<input type="digit" v-model="writeMoney" class="payInput" @input="oninput"  :maxlength="moneyMaxLeng" />

data() {
  return {
    writeMoney: "",
    moneyMaxLeng: 8//规定最大可输入的长度
 }
},
//输入内容验证
oninput(e) {
    this.$nextTick(() => {
      let val = e.target.value.toString();
      val = val.replace(/[^\d.]/g, ""); //清除"数字"和"."以外的字符
      val = val.replace(/.{2,}/g, "."); //只保留第一个. 清除多余的
      val = val.replace(/^0+./g, '0.');
      val = val.match(/^0+[1-9]+/) ? val = val.replace(/^0+/g, '') : val
      val = (val.match(/^\d*(.?\d{0,2})/g)[0]) || ''

      if (val.includes(".")) {
        let numDian = val.toString().split(".")[1].length;
        if (numDian === 2) {
          this.moneyMaxLeng = val.length;
        }
      } else {
        this.moneyMaxLeng = 8;
      }
      this.writeMoney = val;
    });
},
复制代码

29、Vue中使用transform的translateX来实现下滑线跟随导航。

1、template中的代码

<ul class="tab" :style="{height: tabheight}">
    <li
          ref="iWidth"
          v-for="(item,index) in tabList"
          :key="index"
          :class="{'on': checkindex == index}"
          @click="checkli(index)"
    >{{item}}</li>
    <i :style="{transform:`translateX(${iWidths/2+checkindex*iWidths}px) translateX(-50%)`}"></i>
 </ul>
复制代码

2、css

ul.tab {
  height: 1000px;
  width: 100%;
  border-bottom: 1px solid #eeeeee;
  line-height: 1rem;
  font-size: 0.32rem;
  color: #333333;
  display: flex;
  position: relative;
  overflow: hidden;
  transition: all 0.5s;
}
.tab li {
  flex: 1;
  text-align: center;
  transition: all 0.5s;
}
.tab li.on {
  color: #da0428;
}
.tab i {
  width: 0.6rem;
  height: 0.05rem;
  border-radius: 0.03rem;
  background: #da0428;
  bottom: 0;
  position: absolute;
  transition: all 0.5s;
}
复制代码

3、JS代码(实现的主要思路就是通过给元素添加ref属性通过this.$refs.xxx获取此元素的宽度,再配合transform中的translateX属性进行使用。)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值