程序员思维导图、web初学者必备、web前端知识集锦-不断更新中...
export function isWechat() {
const ua = window.navigator.userAgent.toLocaleLowerCase()
return !!ua.match(/MicroMessenger/i)
}
/*
键盘弹起处理
ios 键盘弹起,window.innerHeight 不变,body、html 的 clientHeight、offsetHeight 增大,页面整体上移,暂不做特殊处理
android 键盘弹起,window.innerHeight、body、html 的 clientHeight、offsetHeight 均变小,需要将 input 框滚动到可视区
*/
const originHeight = document.documentElement.clientHeight || document.body.clientHeight
window.addEventListener('resize', () => {
const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight
// android 键盘弹起
if (originHeight > resizeHeight) {
// 将元素滚动到可视区域
const activeElement = document.activeElement
const tagName = activeElement.tagName
if (tagName === 'INPUT' || tagName === 'TEXTAREA') {
activeElement.scrollIntoViewIfNeeded()
}
}
}, false)
1. 父组件滚动时,只需要添加属性overflow: scroll;height: 100vh; 2. 子组件要滚动时,设置overflow: scroll;后还需要确定它的滚动高度,如maxHeight: 580px等等,反正滚动两个条件高度和设置滚动属性,重复3万篇 3. calc 动态高度 当组件无法撑满屏幕时,可以通过calc,如height: calc(100vh - 3.4rem), 这里的3.4rem是已知减去的高度
5.部署rms引发问题思考...
1. 部署文件不生效,首先想到的问题是部署的文件路径对不对,重复3.1万篇... 2. 有没有/需不需要刷新CDN。 3. 代码是否在有错误,打包是否成功。 4. 第一次生效了,其它时间没有生效,很可能是你的文件路径部署错了,不要部署根目录下,如根目录是/c,要部署在具体文件夹下如/c/项目目录。
6.清除sourcetree远程无用的分支
解决方法: 1、进入对应目录下,使用 git remote show origin 命令查看本地仓库追踪远程仓库的状态 2、使用 git remote prune origin 清除所有失效的远程分支或根据提示删除特定失效分支 3、重启 SourceTree 即可。
const itemFunction = function (){console.log("功能保持")} itemFunction.keep = true // 组件方法如: {click:itemFunction }
8. background-repeat: no-repeat; 设置背景时一定要加上这个属性,背景不重复,要不在移动端不同手机会出现怪异的现象
9.grid布局,待更新...
.container {
width: 300px;
height: 200px;
margin: 20px;
padding: 20px;
border: 1px solid #eee;
font-size: 13px;
color: #555;
}
.c-red {
color: red;
}
p {
background-color: rgba(189, 227, 255, 0.28);
padding: 2px 5px;
}
/*单行*/
.single {
width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-break: break-all;
}
/*多行*/
.mutiple {
display: -webkit-box; /*重点,不能用block等其他,将对象作为弹性伸缩盒子模型显示*/
-webkit-box-orient: vertical; /*从上到下垂直排列子元素(设置伸缩盒子的子元素排列方式)*/
-webkit-line-clamp: 4; /*行数,超出三行隐藏且多余的用省略号表示...*/
line-clamp: 4;
word-break: break-all;
overflow: hidden;
max-width: 100%;
}
<!-- 单行和多行溢出处理 -->
11. 解决安卓手机字体不加粗
// 设置优先级 font-weight: bold !important
12.实现菜单的展开与收起及modal视图
12.1 展开与收起
部分代码如下: // 1. div 部分 <nav id="navbar"> <div class="logo"> <img src="https://randomuser.me/api/portraits/men/76.jpg" alt="user" /> </div> <ul> <li><a href="#">Home</a></li> <li><a href="#">Portfolio</a></li> <li><a href="#">Blog</a></li> <li><a href="#">Contact Me</a></li> </ul> </nav> <!-- 菜单按钮 --> <button id="toggle" class="toggle"> <!-- 样式 --> <i class="fa fa-bars fa-2x"></i> </button> // 2. 左侧菜单栏,展示的菜单栏,设置为固定,宽度为200px,默认隐藏起来,设置水平偏移-100%,transform: translateX(-100%);也就是隐藏 css部分 :root { --modal-duration: 1s; --primary-color: #30336b; --secondary-color: #be2edd; } nav { background-color: var(--primary-color); border-right: 2px solid rgba(200, 200, 200, 0.1); color: #fff; position: fixed; top: 0; left: 0; width: 200px; height: 100%; z-index: 100; /* 移动-100%,也就是隐藏掉 */ transform: translateX(-100%); } // 重要,这里的css是紧挨着的,不可以有空格,水平向右偏移200px,也就是左侧菜单栏的宽度 body.show-nav { /* Width of nav */ transform: translateX(200px); } // 3. 调用js实现dom的单击事件 // 3.1 先找到菜单div const toggle = document.getElementById('toggle'); // 菜单按钮 const navbar = document.getElementById('navbar'); // navbar // 3.2 实现单击 function closeNavbar (e) { if (document.body.classList.contains('show-nav') && e.target !== toggle && !toggle.contains(e.target) && e.target !== navbar && !navbar.contains(e.target)) { document.body.classList.toggle('show-nav'); document.body.removeEventListener('click', closeNavbar); }else if (!document.body.classList.contains('show-nav')) { document.body.removeEventListener('click', closeNavbar); } } toggle.addEventListener('click', () => { document.body.classList.toggle('show-nav'); // 可以实现左边菜单关闭与打开 document.body.addEventListener('click', closeNavbar); })
展开
收起
12.2 modal视图(添加删除css,classList)
13.vue-cli从搭建到优化
https://juejin.cn/post/6844903760917954567#heading-14
14. el-tabel 校验
// 关键代码如下: <el-form :model="form" ref="form" :rules="formRules"> <el-table :data="sccForm.tableList" border> <el-table-column label="测试" width="200" align="center"> <template slot-scope="scope"> <el-form-item :prop="'tableList.' + scope.$index + '.test'" :rules="sccFormRule.scc2Pre"> <input v-model="scope.row.scc2Pre" class="input-card"/> </el-form-item> </template> </el-table-column> </el-table> </el-form> // data data () { return { form: { tableList: [] }, formRules: {test: [{ required: true, message: 'test不能为空', trigger: 'blur' }]} } }
15. dialog 对话框中滚动
// 内容滚动 ::v-deep vue专用 ::v-deep .pub-dialog-class { .el-dialog__body{max-height: 60vh;overflow: auto;} // .el-form-item { // margin-bottom: 15px; // input { // width: 180px; // height: 36px; // border: 1px solid #DCDFE6; // color: #7e7878; // transition: border-color .2s cubic-bezier(.645,.045,.355,1); // border-radius: 4px; // outline: none; // padding: 0 10px; // } // } } this.$nextTick(() => { let container = this.$el.querySelector('.el-table__body-wrapper'); container.scrollTop = container.scrollHeight; })
16. 使用swiper3 及swiper时要注意,使用属性loop:true,不能在dom上使用点击事件,点击的话,会导致第一张滑到最后一张时不能点击
需要在swiper里实现onClick事件
if (this.swiper) { this.swiper.destroy(); this.swiper = null; } this.swiper = new Swiper(this.$refs.swiperDom, { direction: 'vertical', speed: 300, autoplay: 3000, autoplayDisableOnInteraction: false, observer: true, observeParents: true, onClick: (swiper, event) => { this.itemClick(this.swiper.realIndex); }, loop: true }); // css样式调整 .swiper-pagination-bullet { margin: 0 13px; }
17. el-upload 自定义上传,部分关键代码
data () { return { list:[{name: 'cs1', id: '5'}, {name: 'cs1', id: '24'}], selectModule: '', // 选择某列表中一个 selectModuleName: '', // 下载的名称 selectModuleUrl: '' // 下载的地址 } } <div> <span>下载模版:</span> <el-select v-model="selectModule" @change="handleSelectModule" placeholder="请选择下载模板" style="margin-left: 20px; width: 300px; height: 36px;"> <el-option v-for="item in list" :key="item.id" :value="item.id" :label="item.name" /> </el-select> <a :href="selectModuleUrl" :download="selectModuleName">下载</a> </div> handleSelectModule (val) { // 获取模版下载地址 const params = { fileId: val, type: 1 } downLoadExcel(params, '下载地址').then(res => { var reader = new FileReader() reader.readAsText(res.data, 'gbk') const _this = this reader.onload = function (e) { try { var result = JSON.parse(reader.result) console.log(result) _this.$message({ type: 'error', message: '下载模版不存在' }) } catch (err) { const url = window.URL.createObjectURL(res.data) let proName = '' for (let i = 0; i < _this.list.length; i++) { if (_this.list[i].proID === val) { proName = _this.list[i].proName break } } _this.selectModuleUrl = url _this.selectModuleName = `${proName}下载模版名称.xls` } } }).catch(error => { console.log(error) }) }, // 下载方法 import axios from 'axios' function downLoadExcel (params, url) { console.log(params, url) return axios({ baseURL: `${API_ROOT}`, // 下载的基本地址, url是拼接下载地址(后缀), 如 http://www.baidu.com/downUrl withCredentials: true, crossDomain: true, url: url, method: 'post', responseType: 'blob', params: params }) } // 上传 data () { return { size: null, // 文件大小 fileInfo: { name: '', file: undefined }, id: '' dialogVisible: false } } <input ref="fileUpload" type="file" @change="fileUpload($event)" style="width: 0px; height: 0px;visibility: hidden;"/> <span v-if="!(fileInfo.name)" @click="handleCheckFile" class="opItem">请选择上传类型</span> <span class="opItem">{{fileInfo.name}}</span> <div class="dialog-content"> <el-select filterable v-model="selectProject" @change="handleSelectModule" placeholder="请选择上传项目" style="margin-left: 20px; width: 300px; height: 36px;"> <el-option v-for="item in list" :key="item.id" :value="item.id" :label="item.name" /> </el-select> 上传: handleSelectModule () { // 调用下载方法 如上 } handleCheckFile () { this.$refs.fileUpload.click() }, fileUpload (e) { // console.log(e.target.files[0]) // let type = e.target.files[0].name.split('.')[1] this.fileInfo.name = e.target.files[0].name this.fileInfo.file = e.target.files[0] this.size = e.target.files[0].size // this.size = bigSize / 1024; this.$refs.fileUpload.value = null }, uploadList () { if (this.size > 30 * 1024 * 1024) { this.$message({ type: 'error', message: '文件不能超过30M' }) return } this.dialogVisible = false uploadFileAsync({ file: this.fileInfo.file, id: this.id, url: '/xxxx/uploadExcel' }) .then(res => { console.log('---成功操作') }) .catch(error => { this.fileInfo = { name: '', file: undefined } this.$message({ type: 'error', message: error.responseMsg }) }) }, function uploadFileAsync (params) { const { file, id, url } = params var formData = new FormData() formData.append('uploadFile', file) return axios({ baseURL: `${API_ROOT}`, url: url, data: formData, timeout: 600000, method: 'post', headers: { 'Content-Type': 'multipart/form-data' }, withCredentials: true, crossDomain: true, params: { id: id } }).then(res => { return Promise.resolve(res.data) }).catch(error => { console.log(error) return Promise.reject(new Error({ responseCode: '20089', responseMsg: '请求出错了,请稍后再试' })) })
18. encodeURIComponent() 浏览器转码问题要注意,特殊字符如,容易转换失败,传给后端无法解析
19. vue h5 移动端滚动不流畅,不丝滑,可以试试加上下面的css
overflow: auto;
-webkit-overflow-scrolling: touch;
20. flex布局设置 ellipsis, css如下
.item { display: flex; align-items: center; .item-text { overflow: hidden; flex: 1; white-space: nowrap; text-overflow: ellipsis; } }
21. textarea的哪些坑
1. 高度自适应,textarea本身是不会自适应的,超过高度会滚动,这里我们就从滚动入手,最好不好用自适应高度
TODO: 目前撤消文字的话高度没有变还没有解决;
TODO: 回显时重置textarea的高度还没有解决,比如说已经发布的文字重新编辑时的回显。
解决方法很简单:就是把它的滚动高度赋值给高度,上源码如下:
// 其实就下面几行代码,是不是很简单
<div>
<textarea class="textarea"
onpropertychange="style.height=scrollHeight+'px';"
oninput="style.height = scrollHeight+'px';"></textarea>
</div>
核心代码就这onpropertychange="style.height=scrollHeight+'px';" oninput="style.height = scrollHeight+'px';"
完整代码:这里为了textarea,走了不少弯路,还用div来实现,不过发现了不少坑,换行的坑还没有埋,所以还是使用原生方法,无坑
2. maxlength 算字数限制时,超过2个换行符,字数就失效,Vue实现动态显示textarea剩余文字数量,解决办法如下:
<template>
<div id="app">
<div class="discussWrap">
<textarea class="description" @input="descInput" cols="33" rows="8" v-model="des"></textarea>
<div class="font-count">{{countNum}}/50</div>
</div>
</div>
</template>
methods: {
descInput(){
if(this.desc.length>50){
this.desc=this.desc.slice(0, 50)
}
this.countNum= this.desc.length
}
}
需要安装vue,然后导入使用
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title></title> <meta name="description" content=""> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href=""> <script src="../../node_modules/vue/dist/vue.js"></script> <style> *{ margin: 0; padding: 0; } .input:empty::before{ color:lightgrey; content:attr(placeholder); } .input{ width: 400px; min-height: 40px; outline: 0 none; border-color: rgba(82, 168, 236, 0.8); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); } .textarea { resize: none; overflow-y: hidden; width: 100px; min-height:70px; } </style> </head> <body> <!-- contenteditable 换行问题还没有解决 坑1: 换行 坑2: 粘贴有格式的东西,需要去格式,例子中已经解决 --> <!-- <div id="app"> <div>顶面</div> <div> <div class="input" contenteditable placeholder="请输入文字" @keyup="editnameSet($event)"></div> </div> <div>底面</div> <textarea v-model="app" class="input"></textarea> </div> --> <!-- 单击输入框发现也会触发onpropertychange,输入一个值同样也会触发这个事件,这就证明了,只要有属性的值被修改就会触发该事件。 oninput 事件在用户输入时触发。--> <div id="app"> <div>顶部</div> <div> <textarea class="textarea" onpropertychange="style.height=scrollHeight+'px';" oninput="style.height = scrollHeight+'px';"></textarea> </div> <div>底部</div> </div> <script> //兼容处理,统一换行时的元素渲染 // document.execCommand("defaultParagraphSeparator", false, "div"); new Vue ({ el: '#app', data () { return { app: '2233' } }, mounted () { // const input = document.getElementsByClassName('input'); // if (input) { // console.log('----------input', input[0].innerHTML = this.app); // input.target.innerText = this.app; // } // const areas = document.getElementById('textarea'); // areas.addEventListener('input', function() { // this.style.height = this.scrollHeight+'px'; // }, false); }, watch: { app (newval) { const textarea = document.getElementsByClassName('textarea')[0]; console.log('----te', textarea.style); // textarea.style.height = 'auto';// 1. 让 textarea 的高度恢复默认 // textarea.style.height = textarea.scrollHeight + 'px';// 2. textarea.scrollHeight 表示 *textarea* 内容的实际高度 } }, methods: { editnameSet (set) { var re=/<[^>]*>/g; //清空标签的正则 const str = set.target.innerText.replace(re,"") set.target.innerText = str; console.log(); }, changeContent (val) { console.log(val); } } }) </script> </body> </html>
22.backImage和image的使用遇到的问题
1.background-image 使用时只显示颜色没有显示图片,怎么回事,你没有加下面属性,完整版
height: 184px;
background-image: url('../../assets/img/announcement/announcement-template-ybtz.png');
background-position: top left; // 位置
background-size: 100%;
background-repeat: no-repeat; // 不重复
2. image加载图片方法
第一种: 固定图片src直接添加地址 <img class="nd-img nd-img00" src="../assets/img/no_task_img00.png" alt="">
第二种: 动态添加图片,这里用到require, item的遍历数组的每一页取的图片名称
<img :src="require(`../assets/img/approve_ing${item.name}.png`)">
但require 的优化方便,快捷;缺点:会把导入的图片加载成base64,占用空间,加载图片比较慢
3. background-image加载图片方法
<div class="template-img" :class="handleTemplateBgImage(item)">
</div>
methods: {
handleTemplateBgImage (item) {
let tempClass = `bg-img-${item.imgName}`;
if (parseInt(this.templateId) === item.templateId) {
tempClass = `${tempClass} active`;
}
return tempClass;
},
}
css部分:
.bg-img-template-ybtz {
background-image: url(~@/assets/img/announcement/ybtz.png) !important;
}
.bg-img-template-xb {
background-image: url(~@/assets/img/announcement/xb.png) !important;
}
.bg-img-template-yjbb {
background-image: url(~@/assets/img/announcement/yjbb.png) !important;
}
.bg-img-template-jctg {
background-image: url(~@/assets/img/announcement/jctg.png) !important;
}
.template-img {
width: auto;
height: 180px;
margin-top: 24px;
margin-bottom: 32px;
border: 2px solid #ffffff;
border-radius: 12px;
background-position: top left;
background-size: 100%;
background-repeat: no-repeat;
}
23. filter的使用
filter的使用
1. 创建filter
创建文件夹filters,创建index.js
const SelectTemplate = {
TEMPLATE_LIST: [
{ text: '模版一', imgName: 'xb', templateId: 2 },
{ text: '模版二', imgName: 'yjbb', templateId: 3 },
{ text: '模版三', imgName: 'template-jctg', templateId: 4 },
{ text: '模版四', imgName: 'template-ybtz', templateId: 1 }
]
};
export default Template;
或
import { templateBg } from '../utils/enum';
const filters = {
// 选择模板背景
filterTemplateBg: value => {
return `swiper-slide-${templateBg[parseInt(value)]}`;
}
};
export default filters;
index.js文件
import Template from '../utils/Template';
const tempList = SelectTemplate.TEMPLATE_LIST;
export const filterTemplateLabel = value => {
const obj = tempList.filter(item => {
return item.templateId === parseInt(value);
});
if (obj && obj.length) {
return obj[0].text;
} else {
return tempList[tempList.length - 1].text;
}
};
export const filterTemplateBgColor = value => {
const obj = tempList.filter(item => {
return item.templateId === parseInt(value);
});
let bgColor = '';
if (obj && obj.length) {
bgColor = obj[0].imgName;
} else {
bgColor = tempList[tempList.length - 1].imgName;
}
return `bg-${bgColor}`;
};
2. 全局引用
main.js中引用
import * as filters from '../platform/filters';
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key]);
});
3. 使用
<div class="input-value">{{ currentTemplateId | filterTemplateLabel }}</div>
<div class="announcement-form" :class="currentTemplateId | filterTemplateBgColor"></div>
25. 千分位处理函数
function formatNumber(num) {
if (isNaN(num)) {
throw new TypeError("num is not a number");
}
return ("" + num).replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, "$1,");
}
26. 本地后管访问测试环境数据,使用代理代码如下:
vue.config.js
devServer: {
proxy: {
// proxy all requests starting with /api to jsonplaceholder
'/api': {
target: 'http://xxx.com.cn/', // 代理接口,这个是测试环境接口
changeOrigin: true,
logLevel: 'debug',
pathRewrite: {
'^/api': '' // 路径中字符替换
}
}
}
}
本地develop设置

27. js forEach循环调用异步方法,如何实现同步,参考文章:https://blog.csdn.net/alex_programmer/article/details/104383843
array.forEach(async (item, index) => {
const res = await requestUrl({item.udmp})
if (res && res.responseCode === '000000') {
url = (res.data && res.data.url) || ''
}
... 逻辑代码 push(url)
})
28.resolve拼接路径,一般用于链接跳转或读取生产或测试环境的图片
router.resolve() 的解释和用法
const resolved: {
location: Location;
route: Route;
href: string;
} = router.resolve(location, current?, append?)
解析目标位置 (格式和 <router-link> 的 to prop 一样)。
current 是当前默认的路由 (通常你不需要改变它)
append 允许你在 current 路由上附加路径 (如同 router-link)
上面的内容为官网中的说明,可以得知其作用是:返回一个完整的路径信息。
location 参数为必填项;current 和 append 为可选参数,暂时没有遇到过相关用法。
应用场景
const routeUrl = this.$router.resolve({
name: 'ListDetail',
query: {
type: '001',
id: id
}
})
// location.origin 代表ip地址如http:www.baidu.com,location.pathname相当于具体的子页面如zhidao,/zhidao***,routeUrl.href具体的列表详情信息
let url = `${location.origin}${location.pathname}${routeUrl.href}`
windows.open(url, '_blank')
29.伪元素动态获取内容,待更新...
30. lang="scss" node-sass,sass-loader的安装
安装失败可能是最新版本问题,可以安装指定版本
cnpm install node-sass --save

浙公网安备 33010602011771号