Vue常用技巧
三元表达式绑定class或style
<div :class="['classify',current=='0' ? 'active' : '']">test</div>
//注意:数组中的classify如果不加引号的话,代表的是data中的一项,并不是类名,将classify加上双引号,变成字符串就可以变成类名
<div :class="['classify', isActive ? 'active' : '']">xx</div>
<div :style="{'cursor': isCreate ? 'not-allowed' : ''}">test</div>
<div :style="{'cursor': isCreate ? 'not-allowed' : '','margin-right':'-8px'}">test</div>
class字符串拼接
<div :class="'classify'+(current=='0' ? ' active' : '')">test</div>
注意:
active
前要加一个空格(必须有),字符串拼接时,两个字符串之间要有空格
vue项目中实时搜索功能的优化——减少请求次数
如果300ms内没有继续输入,那么300ms后就发送一次请求;如果在300ms内继续输入了内容,那么就会删除上次定时器,重新开始计时,直到300ms内没有继续输入时再发送请求
search(val) {
if (this.timer) {
clearTimeout(this.timer);
}
if (val) {
this.timer = setTimeout(() => {
this.getData();
}, 300);
} else {
// 输入框中的内容被删为空时触发,此时会清除之前展示的搜索结果
this.getData();
}
}
动态绑定dom元素id
<li v-for="(item,index) in arr" :id="setId(index)" :key="index"></li>
export default {
methods:{
setId(i){
return `tab${i}`
}
}
}
<div v-for="(item,index) in list" :key="index" >
<div :id="'tab'+index"></div>
</div>
vue阻止子级元素的click事件冒泡(stop)
<div @click="test1()">
<span @click.stop="test2()">按钮1</span>
<span>按钮2</span>
</div>
点击
div
里面的按钮1,就不会触发div
绑定时间test1()
方法
本地vue-router模式设置为mode:'history'时,页面空白,无法加载
vue.config.js
如果配置了publicPath
,则使用history
模式时要配置对应的base: process.env.BASE_URL
const router = new VueRouter({
base:process.env.BASE_URL,
mode: 'history',
})
process.env
使用
{
BASE_URL: "/", //vue.config.js如果配置了publicPath就是配置的路径,没有配置就是'/'
NODE_ENV: "development",//默认开发环境是development,生产环境(上线用)是production
}
vue表单校验
const validateRules = {
// 是否为空
isNonEmpty(value, errorMsg) {
return value === '' ? errorMsg : void 0;
},
// 最小长度
minLength(value, length, errorMsg) {
return value.length < length ? errorMsg : void 0;
},
// 是否手机号
isMoblie(value, errorMsg) {
return !/^1(3|4|5|6|7|8|9)[0-9]{9}$/.test(value) ? errorMsg : void 0;
}
};
class Validator {
constructor() {
this.cache = [];
}
add(value, rules) {
for (const rule of rules) {
let rulesFunc = rule.ruleFunc.split(':');
let errorMsg = rule.errorMsg;
this.cache.push(() => {
let ruleFunc = rulesFunc.shift();
rulesFunc.unshift(value);
rulesFunc.push(errorMsg);
return validateRules[ruleFunc].apply(value, rulesFunc);
});
}
}
validate() {
for (let validatorFunc of this.cache) {
let errorMsg = validatorFunc(); //开始校验
if (errorMsg) {
return errorMsg;
}
}
}
}
export default Validator;
在组件中使用
import Validator from './xxx.js';
使用方法
valid() {
let validator = new Validator();
validator.add(this.tel, [
{
ruleFunc: "isNonEmpty",
errorMsg: "手机号不能为空",
},
{
ruleFunc: "isMoblie",
errorMsg: "号码格式错误",
},
]);
validator.add(this.name, [
{
ruleFunc: "isNonEmpty",
errorMsg: "姓名不能为空",
},
]);
let errorMsg = validator.validate();
return errorMsg;
}
next() {
const errorMsg = this.validatorFunc();
if (errorMsg) {
this.$toast(errorMsg);
return;
} else {
this.$ajax();
}
}
vue自定义指令
- 文本内容复制指令
v-copy
使用该指令可以复制元素的文本内容(指令支持单击复制 v-copy、双击复制 v-copy.dblclick、点击icon复制 v-copy.icon 三种模式),不传参数时,默认使用单击复制
- 第一种:
import { Toast } from 'vant'
const copy = {
bind(el, { value }) {
el.$value = value
el.handler = () => {
if (!el.$value) {
// 值为空的时候,给出提示。可根据项目UI仔细设计
console.log('无复制内容')
return
}
// 动态创建 textarea 标签
const textarea = document.createElement('textarea')
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
textarea.readOnly = 'readonly'
textarea.style.position = 'absolute'
textarea.style.left = '-9999px'
// 将要 copy 的值赋给 textarea 标签的 value 属性
textarea.value = el.$value
// 将 textarea 插入到 body 中
document.body.appendChild(textarea)
// 选中值并复制
textarea.select()
const result = document.execCommand('Copy')
if (result) {
Toast('复制成功') // 可根据项目UI仔细设计
}
document.body.removeChild(textarea)
}
// 绑定点击事件,就是所谓的一键 copy 啦
el.addEventListener('click', el.handler)
},
// 当传进来的值更新的时候触发
componentUpdated(el, { value }) {
el.$value = value
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('click', el.handler)
},
}
export default copy
使用:
<div class="copy" v-copy="detail.userTel">复制</div>
- 第2种
export default {
bind (el, binding) {
// 双击触发复制
if (binding.modifiers.dblclick) {
el.addEventListener('dblclick', () => handleClick(el.innerText))
el.style.cursor = 'copy'
}
// 点击icon触发复制
else if (binding.modifiers.icon) {
if (el.hasIcon) return
const iconElement = document.createElement('i')
iconElement.setAttribute('class', 'el-icon-document-copy')
iconElement.setAttribute('style', 'margin-left:5px')
el.appendChild(iconElement)
el.hasIcon = true
iconElement.addEventListener('click', () => handleClick(el.innerText))
iconElement.style.cursor = 'copy'
}
// 单击触发复制
else {
el.addEventListener('click', () => handleClick(el.innerText))
el.style.cursor = 'copy'
}
}
}
function handleClick (text) {
// 创建元素
if (!document.getElementById('copyTarget')) {
const copyTarget = document.createElement('input')
copyTarget.setAttribute('style', 'position:fixed;top:0;left:0;opacity:0;z-index:-1000;')
copyTarget.setAttribute('id', 'copyTarget')
document.body.appendChild(copyTarget)
}
// 复制内容
const input = document.getElementById('copyTarget')
input.value = text
input.select()
document.execCommand('copy')
// alert('复制成功')
}
参数Attributes
:
参数 | 说明 | 默认值 | 类型 | 可选 |
---|---|---|---|---|
dblclick | 双击复制文本内容 | / | String | 可选 |
icon | 单击icon复制文本内容 | / | String | 可选 |
使用:
<div v-copy> 单击复制 </div>
<div v-copy.dblclick> 双击复制 </div>
<div v-copy.icon> icon复制 </div>
- 文字超出省略指令
v-ellipsis
export default function (el, binding) {
el.style.width = binding.arg || 100 + 'px'
el.style.whiteSpace = 'nowrap'
el.style.overflow = 'hidden';
el.style.textOverflow = 'ellipsis';
}
参数Attributes
:
参数 | 说明 | 默认值 | 类型 | 可选 |
---|---|---|---|---|
width | 元素宽度 | 100 | Number | 必填 |
使用:
<div v-ellipsis:100> 需要省略的文字需要省略的文字需要省略的文字</div>
- 回到顶部指令
v-backtop
export default {
bind (el, binding, vnode) {
// 响应点击后滚动到元素顶部
el.addEventListener('click', () => {
const target = binding.arg ? document.getElementById(binding.arg) : window
target.scrollTo({
top: 0,
behavior: 'smooth'
})
})
},
update (el, binding, vnode) {
// 滚动到达参数值才出现绑定指令的元素
const target = binding.arg ? document.getElementById(binding.arg) : window
if (binding.value) {
target.addEventListener('scroll', (e) => {
if (e.srcElement.scrollTop > binding.value) {
el.style.visibility = 'unset'
} else {
el.style.visibility = 'hidden'
}
})
}
// 判断初始化状态
if (target.scrollTop < binding.value) {
el.style.visibility = 'hidden'
}
},
unbind (el) {
const target = binding.arg ? document.getElementById(binding.arg) : window
target.removeEventListener('scroll')
el.removeEventListener('click')
}
}
参数Attributes
:
参数 | 说明 | 默认值 | 类型 | 可选 |
---|---|---|---|---|
id | 给需要回到顶部的元素添加的id | / | String | 可选 |
offset | 偏移距离为 height 时显示指令绑定的元素 | / | Number | 可选 |
然后你可以在模板中任何元素上使用新的 v-backtop property,如下表示在 id 为 app 的元素滚动 400px 后显示绑定指令的元素:
<div v-backtop:app="400"> 回到顶部 </div>
也可以这样使用,表示为一直显示绑定指令的元素,并且是全局页面回到顶部:
<div v-backtop> 回到顶部 </div>
- 拖拽指令
v-drag
export default {
let _el = el
document.onselectstart = function() {
return false //禁止选择网页上的文字
}
_el.onmousedown = e => {
let disX = e.clientX - _el.offsetLeft //鼠标按下,计算当前元素距离可视区的距离
let disY = e.clientY - _el.offsetTop
document.onmousemove = function(e){
let l = e.clientX - disX
let t = e.clientY - disY;
_el.style.left = l + "px"
_el.style.top = t + "px"
}
document.onmouseup = e => {
document.onmousemove = document.onmouseup = null
}
return false
}
}
使用:
<div v-drag> 支持拖拽的元素 </div>
如何使用这些指令?
- 建一个
directives
目录,目录下新建index.js
文件用于引入并注册指令 directives
目录下新建copy.js
文件:
const copy = {
bind(el, { value }) {
el.$value = value
el.handler = () => {
if (!el.$value) {
// 值为空的时候,给出提示。可根据项目UI仔细设计
console.log('无复制内容')
return
}
// 动态创建 textarea 标签
const textarea = document.createElement('textarea')
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
textarea.readOnly = 'readonly'
textarea.style.position = 'absolute'
textarea.style.left = '-9999px'
// 将要 copy 的值赋给 textarea 标签的 value 属性
textarea.value = el.$value
// 将 textarea 插入到 body 中
document.body.appendChild(textarea)
// 选中值并复制
textarea.select()
textarea.setSelectionRange(0,textarea.value.length)
const result = document.execCommand('Copy')
if (result) {
alert('复制成功') // 可根据项目UI仔细设计
}
document.body.removeChild(textarea)
}
// 绑定点击事件,就是所谓的一键 copy 啦
el.addEventListener('click', el.handler)
},
// 当传进来的值更新的时候触发
componentUpdated(el, { value }) {
el.$value = value
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('click', el.handler)
},
}
export default copy
3.directives
的index.js
文件中引入copy
指令并注册:
import copy from './copy'
// 自定义指令
const directives = {
copy
}
export default {
install(Vue) {
Object.keys(directives).forEach((key) => {
Vue.directive(key, directives[key])
})
},
}
- main.js:
import Directives from '@/assets/js/directives'
Vue.use(Directives)
Vue组件中如何引入外部的js文件?
- 简单粗暴,直接在index.html中使用全局的方式引入,缺点:用不到的组件中也会加载
- 如果是下载到本地的静态文件,可以使用import 的方式导入
import { xxx } from '../js/xxx.js' //注意路径
- 在Vue组件加载完后,手动操作DOM插入js插件
export default {
mounted() {
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = '你的js文件地址';
document.body.appendChild(script);
},
}
- 使用render方法
export default {
components: {
'xxx-js': {
render(createElement) {
return createElement(
'script',
{
attrs: {
type: 'text/javascript',
src: '你的js文件地址',
},
},
);
},
},
},
}
// 使用 <xxx-js></xxx-js> 在页面中调用
- 封装成js插件,使用
Promise
,js加载成功,调用resolve,js加载失败,调用reject
function loadJs(src) {
return new Promise((resolve,reject)=>{
let script = document.createElement('script');
script.type = "text/javascript";
script.src= src;
document.body.appendChild(script);
script.onload = ()=>{
resolve();
}
script.onerror = ()=>{
reject();
}
})
}
export default loadJs
使用:
import loadJs from '../../utils/base/loadJs'
export default {
mounted(){
loadJs('http://api.map.baidu.com/xxx.js').then(()=>{
// 加载成功,进行后续操作
})
}
}
6.动态替换要加载的js文件
// 导入外部js
import Vue from 'vue'
Vue.component('remote-script', {
render: function (createElement) {
var self = this;
return createElement('script', {
attrs: {
type: 'text/javascript',
src: this.src
},
on: {
load: function (event) {
self.$emit('load', event);
},
error: function (event) {
self.$emit('error', event);
},
readystatechange: function (event) {
if (this.readyState == 'complete') {
self.$emit('load', event);
}
}
}
});
},
props: {
src: {
type: String,
required: true
}
}
});
使用:
// 引入
import 'common/importJs.js'
// html使用的地方
<remote-script src="https://pv.sohu.com/cityjson?ie=utf-8"></remote-script>
配置多环境变量
- 通过
npm run serve
启动本地 , 执行development
- 通过
npm run build
打包正式 , 执行production
在项目根目录中新建.env.*
- .env.development 本地开发环境配置
NODE_ENV='development'
# must start with VUE_APP_
VUE_APP_ENV = 'development'
- .env.production 正式环境配置
NODE_ENV='production'
# must start with VUE_APP_
VUE_APP_ENV = 'production'
在src
新建config/env.*.js
进行变量管理,修改起来方便,不需 要重启项目,符合开发习惯
config/index.js
const config = require('./env.' + process.env.VUE_APP_ENV)
module.exports = config
配置对应环境变量env.development.js
// 本地环境配置
module.exports = {
title: 'vue-h5-template',
baseUrl: 'http://localhost:9018', // 项目地址
baseApi: 'https://test.xxx.com/api', // 本地api请求地址
APPID: 'xxx',
APPSECRET: 'xxx',
}
使用:(根据环境不同,变量就会不同了)
// 根据环境不同引入不同baseApi地址
import { baseApi } from '@/config'
console.log(baseApi)
部署nginx配置相关
root
和alias
的区别
Example:
location /lmyself/ {
alias /var/www/image/;
}
若按照上述配置的话,则访问
http://xxx.xx.xx.xx/lmyself/
时,ningx会自动去/var/www/image/目录找文件
location /lmyself/ {
root /var/www/image;
}
若按照这种配置的话,则访问
http://xxx.xx.xx.xx/lmyself/
时,nginx会去/var/www/image/lmyself/目录下找文件
vue-router history
模式
location / {
try_files $uri $uri/ /index.html;
}
location /lmyself {
try_files $uri $uri/ /lmyself/index.html;
}
nodejs(express)部署nginx配置问题
通过域名访问
nodejs
启动的项目,nodejs
使用pm2
启动,端口号3000。一个最末尾加了/
,一个没有/
,这2个是有区别的:
location /public
{
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_set_header Connection "";
proxy_pass http://127.0.0.1:3000/;
}
如上面的配置,如果请求的
url
是http://servername/public
会被代理成http://127.0.0.1:3000
location /public
{
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_set_header Connection "";
proxy_pass http://127.0.0.1:3000;
}
会被代理到
http://127.0.0.1:3000/public
UI框架(vant、Element UI)组件有回调参数的方法中加入自定义参数
// 例子采用vant DropdownMenu ActionSheet
//第一种
@change='onChange($event,自定义需要传的参数)'
//第二种(原生返回多参数的情况下用这种)
@select="(action,index)=>onSelect(action,index,自定义需要传的参数)"
keep-alive动态缓存页面
首先,在配置router
的地方配置meta
const routes = [
{
path: '/', //首页
name: 'index',
component: Index,
meta: {
title: '首页',
keepAlive:true, //表示该组件缓存
},
},
]
App.vue
<div id="app">
<router-view v-if="!$route.meta.keepAlive"></router-view>
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
</div>
如果从某个页面进来不需要缓存
watch: {
$route(to, from) {
if (["/homenj",'/requirementsdetails'].includes(from.path)) {
to.meta.keepAlive = false;
}
},
},