本文旨在整理和记录从事前端工作的过程中遇到的问题以及解决问题的思路。
内容偏新手
且叙述不深,整理的目的是为了方便以后快速查阅之前遇到过的问题。
持续更新,建议点赞收藏 👍
Vue或者React框架使用过程中的笔记
[Vue]改变父组件标签上属性绑定值的方式
- 方式一 普遍做法,通过在标签上绑定一个事件,在子组件上调用$emit触发该事件进而改变属性值
- 方式二 借助.sync修饰符 这种方式无需再绑定事件
- 父组件
:msg.sync = "msg"
- 子组件
this.$emit('update:msg', '传递的值')
- 不能接受表达式和字面量对象
- 父组件
[Vue]v-model 几个实用的修饰符
- lazy 将双向绑定的input事件变成change事件
- trim 自动过滤首位空白字符
- number 将输入值转为数值类型
[Vue]插槽用法
<slot>后备内容</slot>
- 具名插槽 用
v-slot:default
绑定,必须用在<template>
上,如果只有default则可以用在其他标签上 - 作用域插槽(让插槽内部访问子组件数据)
子组件:<slot :user="user"></slot> 父组件:<template v-slot:default="slotProps"/> 通过 slotProps.user 获取, 也可以使用 ES6 解构语法 {user} / {user: 别名}
[Vue]动态的绑定标签特性
<a v-bind:[dataAttr]="url"></a>
...
data: {
dataAttr: 'href'
}
问题集锦
问题描述 | 解决方法 |
---|---|
vue在本地启动的项目无法通过局域网在手机上查看 | 在运行脚本中加入--host 0.0.0.0 |
本地图片无法正常显示 | 使用 require('本地图片路径') |
打包后的html页面显示空白 | 找到文件 config/index.js 修改 build 配置下的 assetsPublicPath 为相对路径 './' |
清除浏览器默认样式 | 在项目引入 reset.css |
vue中绑定键盘事件 | @keydown.enter 注意绑到按钮上无效 |
改变状态值页面无响应,页面需下一帧才能得到变化 | setTimeout(()=>{...}, 0) vue中还可以使用 this.$nextTick(()=>{...}) |
路由使用history模式无效 | 这种模式需要在服务端有响应的配置 用hash模式可在前端使用路由 |
微信小程序(含uni-app)使用笔记
报渲染层错误 Dom limit exceeded
- 问题描述:使用
picker-view
和<picker-view-column/>
自定义时间组件时,开发者工具出现渲染层错误 undefined 提示,真机上打开调试模式出现 Dom limit exceeded 提示。 - 问题说明:微信小程序的节点有数量限制,超出限制会报这种错误。
- 解决方法:用
v-if
(uni-app) 或者wx:if
先隐藏该组件,在用事件触发显示。 - 扩展:如何用小程序实现类原生APP下一条无限刷体验
使用 iconfont
- 小程序无法解析字体文件(ttf等),需使用在线链接
- 直接在项目中引入该css链接或者复制该链接在浏览器中打开自己新建css文件导入项目
[uni-app] ios app 点击空白处无法使输入框键盘收起
- 官方文档有说明:组件input
[uni-app] 关于用 v-if/v-show 控制 cover-image/cover-view 的问题
- 2020.05.09 HBuilder版本2.6.11.20200409
v-if/v-show
直接使用在cover-image/cover-view
组件上是没有任何问题的- 但是,当
v-if/v-show
使用在包含cover-image/cover-view
组件的父组件(嵌套多层)上时,会出现如下BUG:v-if/v-show
条件由false
变成true
可以正常显示,再由true
变成false
时,cover-image/cover-view
组件会留下残影,无法隐藏。- 并非所有的
cover-image/cover-view
都无法隐藏,反复调试发现,前两个可以正常隐藏//前两个 组件可以正常显示/隐藏 最后一个显示之后会留下“残影” <scroll-view class="search-result" scroll-y v-if="!isShowMap"> ... </scroll-view> <view class="map-choose" v-else> <map :hidden="appHideMap"> <cover-image src="xxx.png" class="traget-point"></cover-image> <cover-view class="back-center" @click="handleBackCenter"></cover-view> <cover-image src="xxx.png" class="back-center icon" @click="handleBackCenter"></cover-image> </map> </view>
- 解决方法:在
v-if/v-show
条件由true
变成false
之前就隐藏掉无法隐藏的组件searchFocus(){ this.appHideMap = true this.$nextTick(() => { this.isShowMap = false }) },
常用操作流程
Vue 中使用 scss 或者 less
npm i sass-loader node-sass
或者npm i less less-loader
- 在 webpack.base.conf.js 中加入
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] } //或者 { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }
vuex 基本使用方法
npm i vuex -D
- 在 src 中创建 store 文件夹,其中包括
index.js、state.js、mutations.js、actions.js、getters.js
五个文件 - index.js: store 的入口文件 向外暴露 vuex store
import Vue from 'vue' import Vuex from 'vuex' import state from './state' import getters from './getters' import mutations from './mutations' import actions from './actions' Vue.use(Vuex) //这行注意不要忘加 const store = new Vuex.Store({ state, getters, mutations, actions }) export default store
- 在 Vue 实例(main.js)中注册 store
- 各个文件的内容
// state.js const state = { test: 1 } export default state //mutations.js const mutations = { setTest(state) { state.test = 2 } } export default mutations //actions.js const actions = { setTestActions: (context) => { context.commit('setTest') } } export default actions //getters.js const getters = { getTest: (state) => state.test } export default getters
- 使用方法
this.$store.state.test this.$store.getters.getTest this.$store.commit('setTest') this.$store.dispatch('setTestActions') //或者使用语法糖 //其中 mapState mapGetters 在 computed 属性中使用 mapMutations mapActions 在 methods 属性中使用 import {mapActions, mapGetters, mapState, mapMutations} from 'vuex' computed: { ...mapState({ // testData: 'test' // 或者 testData: (state) => state.test //要想this指向Vue需要用普通函数形式 }), ...mapGetters({ getData: 'getTest' }) }, methods: { ...mapMutations({ setTestM: 'setTest' }), ...mapActions({ setTestA: 'setTestActions' }) },
vue-router 基本使用方法
npm i vue-router -D
- 配置路由页面
//src/router/index.js
import Vue from 'vue'
import Router from "vue-router";
import Home from "../components/home.vue";
import My from "../components/my.vue";
Vue.use(Router) //注意不要漏了
export default new Router({
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home/:type',
name: 'home'
component: Home
},
{
path: '/my',
name: 'my',
component: My
}
]
})
- 在 Vue 实例(main.js)中注册 router
vue 中 ElementUI 使用方法
npm i element-ui babel-plugin-component -D
- 修改 .babelrc 文件
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime", [
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]]
}
- 引入使用
import {Button} from 'element-ui
//全局注册
Vue.use(Button)
<el-button type="text">按钮</el-button>
vue + typescript 基本使用方法
- vue中写ts 特别难受,参考下作者之前写的这个吧:在 Vue 中使用 Typescript
react中使用 styled-components
- 新建 style.js 文件
import styled from 'styled-components'
export const Container = styled.div `
height: 100%;
.top{
margin-bottom: 10px;
}
.bottom{
height: calc(100% - 42px);
overflow-y: scroll;
}
`
- 使用
import {Container} from './style'
react-redux 基本使用方法
npm i redux react-redux redux-thunk -D
- 在 src 中创建 store 文件夹,其中包括
index.js、reducer.js、actionTypes.js、actionCreators.j
四个文件 - index.js:store 的入口文件 管理 store 文件夹中的内容
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
const store = createStore(reducer, applyMiddleware(thunk))
export default store
- 在 App.js 中 引入
import { Provider } from 'react-redux'
import store from './store'
<Provider store={store}></Provider>
- 各个文件的内容
//actionTypes.js action type 名称的集中管理
export const CHANGE_TEST = 'CHANGE_TEST'
//actionCreators.js 创建 action
import * as actionTypes from './actionTypes'
export const changeTest = () => ({
type: actionTypes.CHANGE_TEST
})
//reducer.jsx 设置默认状态值 对不同的 action 作处理
import * as actionTypes from './actionTypes'
const defaultState = {
test: 1
}
export default (state = defaultState, action) => {
switch (action.type) {
case actionTypes.CHANGE_TEST:
return {
...state,
test: 3
}
default:
return state
}
}
- 使用方法
import {connect} from 'react-redux'
import { changeTest } from '../store/actionCreators'
...
const { testData, changeTestData } = this.props //会被放入props中
...
const mapState = (state) => ({
testData: state.test
})
const mapDispatch = (dispatch) => ({
changeTestData(){
dispatch(changeTest())
}
})
export default connect(mapState, mapDispatch)(App)
react-router-dom 基本使用方法
npm i react-router-dom -D
import {HashRouter, Route, Switch, Redirect} from 'react-router-dom'
- 使用
<HashRouter>
<Switch>
<Route path="/" exact render={() => <Redirect to="home"></Redirect>}></Route>
<Route path="/home" component={Home}>
</Route>
<Route path="/my" component={My}>
</Route>
</Switch>
</HashRouter>
react 中使用 Ant Design
npm i antd -D
- 配置成中文
import {ConfigProvider} from 'antd'
import 'antd/dist/antd.css'
import zhCN from 'antd/es/locale/zh_CN'
// 将日期组件变成中文
import moment from 'moment'
import 'moment/locale/zh-cn'
moment.locale('zh-cn')
<ConfigProvider locale={zhCN}></ConfigProvider>
- 使用
import { Button } from 'antd'
react + typescript 基本使用方法
- 指令
npx create-react-app demo --typescript
或者npx create-react-app demo --template typescript
这种指令直接生成了一个 可以正常使用 ts 的模版 无需自己额外安装依赖和配置 - 手动改装原 react 项目
- 安装
typescript @types/react @types/react-dom
(使用@types/前缀表示我们额外要获取React和React-DOM的声明文件) - 创建一个
tsconfig.json
文件
{ "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "noImplicitAny": true, "module": "commonjs", "target": "es5",//报错无法识别 Set 要改成es6 "jsx": "react" }, "include": [ "./src/**/*" ] }
.js
后缀全部改成.tsx
import React from 'react'
要改成import * as React from 'react'
- 在组件上使用泛型
//这是React.Component的泛型类 class Component<P, S> { readonly props: Readonly<{ children?: ReactNode }> & Readonly<P>; state: Readonly<S>; }
- 安装
git 操作技巧
- 电脑第一次安装git之后,如何快速开始使用
- 配置用户名
git config --global user.name "XXXX"
- 配置邮箱
git config --global user.email "XXXX"
(可在C:/用户/(你的用户名)/.gitconfig 中看到) - 生成SSH秘钥
ssh-keygen -t rsa -C "刚刚配置的邮箱"
,连续按回车即可 - 查看SSH秘钥
cat ~/.ssh/id_rsa.pub
或者 去C:/用户/(你的用户名)/.ssh 中查看 - 将 SSH 秘钥配置到相应的git平台中(github、gitlab、gitee等)
- 现在就可以
git clone
相应平台中的代码了 - 私有库克隆的时候需要输入用户名和密码,如果第一次输错了,后续会直接告诉你没有权限访问,解决方法:
控制面板 - 用户账户 - 管理Windows凭据 - 普通凭据
找到相应的路径编辑或者删除即可。
- 配置用户名
- 创建本地版本库
>> git init
>> git add <file>
>> git commit -m <message>
- 本地代码首次推送到远程仓库
>> git init
>> git add <file>
>> git commit -m <message>
>> git remote add origin <url>
>> git push origin master
- 克隆拉取代码修改后提交
>> git clone <url>
>> git add <file>
>> git commit -m <message>
>> git push origin master
- 拉取代码冲突时
>> git stash
>> git pull
>> git stash pop
- 给稳定版本打标签
>> git tag 【查看tag】
>> git tag -a v1.0.0 4a808674 -m "v1.0.0" 【给指定commit 打tag,不填则是最近的一次commit】
>> git push origin v1.0.0 【将tag推送到远程】
echarts 基本使用方法
npm i echarts -D
- 新建一个echarts配置文件
//echarts_init.js
import echarts from 'echarts'
echarts.registerTheme('co-op-theme', {
color: ['#5A8BFF', '#5FA944', '#FF2E2E']
})
export default echarts
// 直接引用这个echarts
- 使用
import echarts from '../../echarts_init'
echarts.init(document.getElementById('charts'), 'co-op-theme').setOption({
legend: {
data: ['水表', '电表']
},
tooltip: {
trigger: 'axis',
formatter: (params) => {
return params[0].name + '月</br>' + params[0].seriesName + ':' + (params[0].data === 'null' ? '-' : (params[0].data + '吨')) + '</br>' + params[1].seriesName + ':' + (params[1].data === 'null' ? '-' : (params[1].data + '度'))
},
axisPointer: {
type: 'shadow'
}
},
grid: {
containLabel: true,
height: '80%',
width: '80%',
y2: 0,
x: '10%'
},
yAxis: [
{
type: 'value',
name: '水表(吨)'
},
{
type: 'value',
name: '电表(度)'
},
],
xAxis: {
type: 'category',
data: date,
name: '月份',
axisLabel: {
interval: 0
},
},
series: [
{
name: '水表',
type: 'line',
yAxisIndex: 0,
label: {
normal: {
show: true,
position: 'bottom'
}
},
data: waterData
},
{
name: '电表',
type: 'line',
yAxisIndex: 1,
label: {
normal: {
show: true,
position: 'top'
}
},
data: electricData
},
]
})
<div id="charts"></div>
公众号H5获取微信用户OPENID(前端的操作)
-
点击公众号菜单链接进入项目,可以从该链接的参数中获取到 appid、secret以及需要的一些参数(由后端配置)
-
调用
https://open.weixin.qq.com/connect/oauth2/authorize
,需要传入以下参数参数 描述 appid 从链接获取的appid redirect_uri 获取code成功后要重定向的页面路径 response_type code scope snsapi_base state a-zA-Z0-9,可以使用当前时间戳 connect_redirect 1 #wechat_redirect 必须跟在尾部 - redirect_uri:重定向页面路径,需要传入链接中带入的appid和secret,还可以传入项目中需要参数。
encodeURIComponent('http://XXX/index.html?appid=XXX&secret=XXX&otherparams=XXX#/home')
重定向的路径必须带上域名(localhost不可以)并且需要使用encodeURIComponent编码
- redirect_uri:重定向页面路径,需要传入链接中带入的appid和secret,还可以传入项目中需要参数。
-
跳转成功后 当前路径后会跟上重定向前带过来的 appid secret code 将他们传给 api.weixin.qq.com 即可拿到openid (在服务端进行)
微信小程序登录态获取session(前端的操作)
其他命令速查
命令内容 | 解释说明 |
---|---|
vue init webpack demo (demo 为项目名称) | 用 vue cli2 快速生成一个 vue 项目模版 |
npx create-react-app demo (demo 为项目名称) | 快速生成一个react项目模版,create-react-app test 命令已不推荐使用,请先 npm uninstall -g create-react-app 移除全部命令,再npx create-react-app test 下载模版 |
ES6常用语法
Promise的基本用法
var p = new Promise((resolve, reject) => {
//new 后立即执行的操作
resolve() //异步执行成功后要进行的操作,在then中进行
reject()//异步执行失败后要进行的操作,在catch中进行
})
p.then().catch().finally()
Promise.all([p1, p2, p3]).then().catch() //全部拿到结果后再执行
Promise.race([p1, p2, p3]).then().catch() //有一个拿到结果就执行
export 与 export default
问题集锦
语法格式 | 简单描述 |
---|---|
Array.includes() | 检测数组中是否包含指定项,返回布尔值 |
()=>{} | 箭头函数,注意它没有自己的作用域 |
`xxx${var}` | 模版字符串 |
new Set(array) ==> 使用[...array] | 数组快速去重 |
Array.from(new Set(array)) | 将Set变成普通数组 |
Object.keys().length | 判断对象是否为空 |
js技巧
雷区
- sort函数排序会改变原数组的值!如果想要保证原数组不变,需要拷贝一份。(注意:
let newList = list
不会改变对象指针,不能视为拷贝! 浅拷贝可以用let newList = [...list]
)
全屏化web页面
-
使用场景:工作中遇到需要把
前端框架开发的web看板项目
嵌入后台jsp开发的web管理系统
当中,并且需要该看板页面能够全屏展示
-
此时如果按 F11 全屏会导致 管理系统的标签页以及导航栏都会全屏,而无法单独的全屏展示看板
-
解决方法:在看板页面中加一个全屏化的小图标用于切换全屏/非全屏状态,点击运行js相应代码
【注意】火狐浏览器中无法全屏:需要在包含前端看板 html 的`iframe` 上加上属性 `allowfullscreen="true"
openKanban(){ let state = false //是否全屏状态 //全屏 function fullScreen(el) { var rfs = el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullScreen, wscript; if(typeof rfs != "undefined" && rfs) { state = true rfs.call(el); return; } if(typeof window.ActiveXObject != "undefined") { wscript = new ActiveXObject("WScript.Shell"); if(wscript) { state = true wscript.SendKeys("{F11}"); } } } //退出全屏 function exitFullScreen(el) { var el= document, cfs = el.cancelFullScreen || el.webkitCancelFullScreen || el.mozCancelFullScreen || el.exitFullScreen, wscript; if (typeof cfs != "undefined" && cfs) { state = false cfs.call(el); return; } if (typeof window.ActiveXObject != "undefined") { wscript = new ActiveXObject("WScript.Shell"); if (wscript != null) { state = false wscript.SendKeys("{F11}"); } } } if (state) { // #app-body 获取的是 <body></body> 意思是将 body的内容全屏 exitFullScreen(document.getElementById('app-body')) }else{ fullScreen(document.getElementById('app-body')) } },
函数柯里化
- 参考
- 把有多个参数的函数变成只有一个参数的函数,这个函数返回一个新函数,这个新函数接受剩余的参数并且返回最后的结果,这个过程就叫做柯里化。
- 通用封装
let currying = function(fn) {
let args = Array.proptotype.slice.call(arguments, 1) //拿到除第一个参数之外的其他参数
return function(...rest){
let allArgs = args.concat(...rest)
return fn.apply(null, allArgs)
}
}
- 参数复用:将封装函数的第一个参数复用,柯里化后只需要传入要出的数据即可 其实是对函数功能进一步的划分
// 正常正则验证字符串 reg.test(txt)
// 函数封装后
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// Currying后
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
- 延迟执行,提前确认
防抖和节流
- 防抖:指触发事件后在规定时间内回调函数只能执行一次,如果在规定时间内又触发了该事件,则会重新开始算规定时间。
- 节流:当持续触发事件时,在规定时间段内只能调用一次回调函数。如果在规定时间内又触发了该事件,则什么也不做,也不会重置定时器.
移动端h5页面监听手机横屏
//在页面加载mounted完成以及在created触发横屏事件时调用
window.onorientationchange = () => {
this.orient()
}
orient(){
if (window.orientation == 90 || window.orientation == -90) {
//横屏
this.isLandscape = true //isLandscape状态表示是否横屏
return false;
}
else if (window.orientation == 0 || window.orientation == 180) {
//竖屏
this.isLandscape = false
return false;
}
}
复制点击内容到剪切板
- 先在页面中创建一个看不见的input文本框
- 将要复制的内容放到 input 的 value中
- 调用js复制代码(以vue中的使用情况为例)
<input disabled id='input' type="text" v-model="copyResult">
#input{
position: absolute;
top: 0;
right: -1000px;
z-index: -10
}
this.copyResult = url
this.$nextTick(() => {
document.getElementById('input').select()
let state = document.execCommand('copy')
if (state) { //state为true则表示复制成功了
this.$notify({
message: '详情链接已复制到剪切板,请根据需要自行打开',
type: 'success'
})
}else{
this.$notify({
message: '无法获取链接',
type: 'danger',
duration: 1000
})
}
})
运算符优先级
- && > || > ... ? ... : ...(一直以为
与和或 是同级的 混合使用从左至右即可其实不是 与的优先级大于或如 true || false && false 结果为 true
)
对象拷贝
- 深拷贝
- JSON.parse(JSON.stringify(obj)) 对象包含可序列化值并且没有循环引用时有效
- 浅拷贝
- {...obj}
- Object.assign({}, obj) 拷贝的仅为对象的指针
对URI编码和解码
- encodeURI()、decodeURI()
- encodeURIComponent()、decodeURIComponent() 可对特殊字符如#,/,¥等起作用
- 注意当使用浏览器地址传参数时(如路由跳转时),如果参数中有中文,需要使用 encodeURIComponent() 编码,因为浏览器url会自动解码一次所以需要编码两次才能生效:
encodeURIComponent(encodeURIComponent(url))
toFixed 出现无限循环小数
Math.floor(xx * 100) / 100
xx.toString().match(/^\d+(?:\.\d{0, 2})?/)[0]
检测数据类型
- 参考
- typeof 检测不了引用类型,数据和对象的检测类型都是 object 并且
typeof null === "object" 为 true
、typeof function(){} === "function" 为 true
- instanceof 只能检测对象的类型
- Object.prototype.toString.call() 都可以正确检测,如
Object.prototype.toString.call([]) === "[object Array]" 为 true
巧用正则表达式
正则表达式 | 简单描述 |
---|---|
str.replace(/\b\d\b/g, '0$&') | 给时间字符串中的单位数字前补零 |
str.replace(/[\u4e00-\u9fa5]/g, '') | 去掉一串字符串中间的中文 |
CSS技巧
box-sizing 失效
- 问题:当flex布局使用
flex:1
属性时box-sizing
设置border-box
无效 - 用高宽百分比值代替
制作文字渐变效果
- 方法一:利用
background-clip: text
按文字轮廓裁剪背景,将背景设置为渐变色,文字设置成透明- 注意
-webkit-background-clip: text;
必须手动添加,框架不会自动补全 - 这种方法火狐浏览器无效果(无视IE),可以复制一层纯色的文字以防止字体透明不可见
- 注意
background: linear-gradient(to right, #04A8F0, #1EAED2, #4177ED);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
- 方法二:利用svg中的linearGradient 制作渐变字体再通过 fill 属性放入div中
- 兼容性好
<span class="titel-center-1">
<svg width="100%">
<defs>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#04A8F0"/>
<stop offset="0.5" style="stop-color:#1EAED2"/>
<stop offset="1" style="stop-color:#4177ED"/>
</linearGradient>
</defs>
<text text-anchor="middle" class="gradient-text-three" x="50%" y="30%">{{$store.state.userInfo.company}}生物安全防控平台</text>
</svg>
</span>
.titel-center-1{
width: 39%;
display: inline-block;
height: 50px;
overflow: hidden;
font-size: 36px;
font-weight: bold;
fill: url(#SVGID_1_); /*关键属性*/
}
在flex布局的情况下 保证内容不溢出 并添加滚动
- 使用场景:某个div采用flex布局,想要在这个div下的某个子div中加入一个滚动列表,且该列表的每一行后边超出内容时加上 ...
- 通常把这个列表的高度设置为100% 无效果,超出的列表项会把容器撑大
- 解决方法:
.parent{
display: flex;
flex-direction: column; //竖直方向 column搭配height 水平方向row+width:0
}
.child{
flex: 1;
overflow: hidden;
height: 0;
}
flex流布局
- 项目开发中经常用到的布局,推荐参考 阮一峰老师的教程,非常详细。
选择器优先级
文本内容超出容器显示省略号
- 单行文本
#content{ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
- 多行文本(背景颜色设为transparent)
#content{ position: relative; overflow: hidden; } #content::after{ content: '...'; position: absolute; bottom: 0; right: 0; }
消除img标签中的图片为空时的边框
img[src=""], img:not([src]){
opacity: 0;
}
制作一个表头固定 表体滚动的表格(需设置高、宽)
- thead, tr 设置
display: table; table-layout: fixed;
- tbody 设置
display: block; overflow-y: scroll;
子元素设置浮动后父元素设置 height: auto 无效
.parent::after{
content: "";
display: table;
clear: both;
}
BFC(块格式化上下文)
- 参考这篇文章 什么是BFC?看这一篇就够了
- 上面这个例子(子元素设置浮动后父元素设置 height: auto 无效)就是创造了BFC
- 如何创建BFC
- float的值不是none
- position的值不是static或者relative
- display的值是inline-block、table-cell、flex、table-caption或者inline-flex
- overflow的值不是visible
设置图片宽高为相对百分比的情况下让宽高相等
.parent{
position: relative;
height: 0;
padding-bottom: 100%;
}
.child{
width: 100%;
height: 100%;
position: absolute;
}
设置图片的填充方式和位置
在less/scss中使用calc()无效
//less中加上~转译符号
height: calc(~"100vh-200px")
//scss中使用插值#{}
height: calc(100% - #{$var})
特殊属性集锦
样式格式 | 简单描述 |
---|---|
text-decoration: line-through | 定义穿过文本的一条线 |
text-align-last: justify | 让文字两端对齐 |
resize: none | 禁止textarea拉伸 |
font-size: 0 | 让 input 的 cursor: pointer 样式生效 |
touch-action: none | 禁用浏览器触摸交互 |
pointer-events: none | 让元素永远不能成为鼠标事件的target |
常用库问题集锦
ElementUI
问题描述 | 解决方法 |
---|---|
图标无法正常显示 | 在 build/utils.js 文件中的 ExtractTextPlugin 插件中加入 publicPath: '../../' |
当输入框的input事件方法传人参数时,无法获取输入框的值 | 如@input="handleInput(scope.$index, index, arguments[0])" 可以使用arguments参数对象获取 |
AntDesign
问题描述 | 解决方法 |
---|---|
日期选择框中的年月日是英文 | import {ConfigProvider} from 'antd' => import 'moment/locale/zh-cn => import zhCN from 'antd/es/locale/zh_CN' => <ConfigProvider locale={zhCN}>...</ConfigProvider> |
Table组件中含有表单控件时无法正常使用状态state | Table的column属性不要定义在状态中,直接用const接收 |
Form表单调用onSubmit提交会刷新浏览器 | 需要在改事件中禁用默认行为e.preventDefault() ,Ant Design 4.0 版本已使用onFinish代替onSubmit Ant Design 4.0 正式版初探 |
Echarts
- 柱状图颜色渐变设置
//在series中加入 //其中 0, 0, 0, 1 四个参数是起始点和结束点的位置 // 如果要实现 135度的,就是 (0, 0, 1, 1) itemStyle: { normal: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#ba7ef3' }, { offset: 1, color: '#965df2' }]), } },
问题描述 | 解决方法 |
---|---|
使用内置的百度地图不显示 | 需要引入 import 'echarts/extension/bmap/bmap' |
Axios
- 利用 qs 库序列化请求参数
qs.stringify({ids: [1, 2, 3]}, { indices: false })
//形式: ids=1&ids=2&id=3
qs.stringify({ids: [1, 2, 3]}, {arrayFormat: 'indices'})
//形式: ids[0]=1&aids1]=2&ids[2]=3
qs.stringify({ids: [1, 2, 3]}, {arrayFormat: 'brackets'})
//形式:ids[]=1&ids[]=2&ids[]=3
qs.stringify({ids: [1, 2, 3]}, {arrayFormat: 'repeat'})
//形式: ids=1&ids=2&id=3
- 参考封装:
import axios from 'axios'
import qs from 'qs'
import {message} from 'antd'
axios.defaults.baseURL = 'http://XXX/'
axios.defaults.withCredentials = true //设置为true 可以让请求带上cookie中的值,用于获取登录状态的SESSION
axios.interceptors.request.use(function (config) {
// 判断请求的类型
// 如果是post请求就把默认参数拼到data里面
// 如果是get请求就拼到params里面
if (config.method === 'post' || config.method === 'put') {
let data = qs.parse(config.data)
config.params = {
...config.params
}
config.data = qs.stringify({
...data
}, {
arrayFormat: 'brackets'
})
}
return config
}, function (error) {
return Promise.reject(error)
})
axios.interceptors.response.use((res) => {
//对响应数据做些事
// if (res.config.method === 'get') {
// return res
// }
if (res.data.code === '0000') {
// if (res.config.method === 'post') {
return res.data.data
// }
} else if (res.data.statusCode === 304) {
window.location.href = '/#/login'
} else {
return Promise.reject(res.data.message)
}
}, (error) => {
return Promise.reject('请求异常')
})
function request(params){
return new Promise((resolve, reject) => {
axios({
url: params.url || '',
method: params.method || 'post',
params: params.params || {},
data: params.data || {},
headers: {
'Content-Type': params.contentType || 'application/x-www-form-urlencoded'
},
paramsSerializer: function (param) {
return qs.stringify(param, {
arrayFormat: 'repeat'
})
},
}).then(res => {
resolve(res)
}).catch(err => {
console.log(err)
message.error(err || '出问题')
reject(err)
})
})
}
export default request
-
某一个请求不希望受到全局拦截器影响:
axios.create()
返回一个不受拦截器影响的axios实例
用它去发起请求。 -
上传文件:
- 需要设置 contentType 为
multipart/form-data
- 通过
FormData
传输文件
let formdata = new FormData() formdata.append('file', e.target.files[0]) //e为 <input type="file"/> onchange 回调参数 axios({ url: '', method: 'post', data: formdata, headers: { 'Content-Type': 'multipart/form-data' } })
- 注意全局拦击器中要过滤掉post请求
multipart/form-data
类型的参数序列化(其实只有application/x-www-form-urlencoded
这种类型需要序列化)
axios.interceptors.request.use((config) => { if (config.method === 'post' && config.headers['Content-Type'] === 'application/x-www-form-urlencoded') { let data = qs.parse(config.data) config.params = { ...config.params } config.data = qs.stringify({ ...data }) } return config; }, (error) => { return Promise.reject(error.toString()); })
- 需要设置 contentType 为
操作DOM
- DOM.getBoundingClientRect().bottom 获取DOM元素相对于可视页面底部的距离
- 注意:元素的 offsetTop、offsetLeft 的值是根据css定位来的,默认是相对 body,出现 relative 会改变这种行为,利用 offsetParent 可查看父元素
- DOMid.scrollTop = DOMid.scrollHeight 可以让元素滚动到最底部
- 一个高度自适应内容且高度有最大值的的textarea
<textarea rows="1" className="input-msg" value={currentContent} type="text" onChange={(e) => setCurrentContent(e.target.value)} onInput={autoTextAreaHeight} onBlur={handleBlur}></textarea>
const autoTextAreaHeight = (e) => {
if (e.target.scrollHeight <= 90) {
e.target.style.height = 'auto'
e.target.style.height = e.target.scrollHeight + "px"
}
}
const handleBlur = () => {
//解决IOS失焦事件先于click事件导致click事件无效
setTimeout(() => {
window.scroll(0, 0)
}, 0)
}
//css
// resize: none;
web端与移动端的兼容性
移动端视频无法自动播放的问题
- 移动端为了节省流量所作出的限制,即使在
video
标签设置了autoplay
也无法自动播放。 - 通常会使用一种方式去 引导用户操作一次页面,如点击按钮,关闭弹框等,在这些事件中调用
play()
方法播放视频。 - 注意,作者发现,安卓每次刷新视频都要重新调用
play()
,而 ios 貌似只需要进入页面调用一次,后续就会自动播放。 - 在web端有些环境下 设置
autoplay
也无法自动播放,通常设置 muted 静音即可解决。
ios 视频播放会自动全屏
- 只需要在 video 标签上 加上属性
webkit-playsinline='true' playsinline='true'
即可阻止全屏
其他问题
问题描述 | 解决方法 |
---|---|
ios端微信h5页面上下滑动时卡顿、长列表快速滑动时页面缺失 | 给相应的列表或者全局 * 加上这个样式-webkit-overflow-scrolling: touch; |
IOS/Safari XXXX-XX-XX 日期无效 | 不支持这种 - 的格式,利用正则替换日期字符串 replace(/\-/g, "/") |
火狐浏览器在文件域中无法发起ajax请求请求json文件 | 首先在浏览器地址栏中输入 about:config 然后找到 security.fileuri.strict_origin_policy 置为 false |
IOS/Safari 使用 transform 时,z-index 无效 | 需要在祖先元素上加上 overflow: hidden; |
浏览器默认字体不一样导致空格显示的长度不一样 | 设置样式 font-family: Times New Roman |
IE 报错 Promise 未定义 | 安装 babel-polyfill 然后在 main.js 中引入 |
手机上H5的网络图片不显示 403 Forbidden | 在入口页面加上<meta name="referrer" content="no-referrer" /> |
- 参考文献
其他
- localStorage 只能存储字符串,存储数组对象需要用JSON转换
- momentJS 配置成中文
moment.locale('zh-cn', xxx)
xxx是中文配置 ['a']=='a' 为true
typeof([]) = 'object'
- 跳过ESlint检测
/*eslint-disable*/ ... /*eslint-enable*/
安装 vue-devtools 调试工具(谷歌)
- 会科学上网的直接去谷歌扩展商城下载即可
- 去github上拉取资源手动安装扩展
- 前往 vue devtools githu地址 下载,注意拉取的是
master分支
的代码!默认git clone 拉的 dev分支,这个分支报错无法正常构建。 npm i
npm run build
- 前往谷歌浏览器中 加载已解压的扩展程序 选择
shells/chrome
- 前往 vue devtools githu地址 下载,注意拉取的是
结束语
暂时就这么点了,有什么遗漏的以后再补充。