乐趣区

前端小姿势

文章包含学习中遇到的问题
小白水平 可能会包含一些错误 或者还没写完 … 或者非最佳实践 仅供参考
1. 父子组件的 v -model

参考 vue 官方文档:点我

父级不需要操作额外代码
子代操作方法一:

这个 vue 的官方 api 会有介绍

model: {
prop: ‘checked’,
event: ‘change’
}
v-on:change=”$emit(‘change’, $event.target.checked)”

子代操作方法二:

props:{
value:[String,Number],
}

handleInput(event) {
const value = event.target.value;
// 通过这个实现 v -model 的 set
this.$emit(‘input’, value);
},

2018/8/21 update: 看了不少文章,这种比较多 以 input 为例子
先简单介绍下 v-model 语法糖

<input
:value=”something”
@input=”something=$event.target.value”
/>
<!– 组件上的简化 –>
<custom-input
:value=”something”
@input=”something=$event”
>
</custom-input>

<!– 子组件 –>
<input type=”text” :value=”currentValue” @input=”handleInput”>
<!– 1. 子组件设置 props 的 value –>
props:[‘value’],// 接受父组件传递过来的 value 值
<!– 2. 向父组件传递改变的值 –>
methods:{
handleInput(event){
let value =event.target.value;
this.$emit(‘input’,value);// 触发 input 事件,并传入新的值
},
setCurrentValue(value) {
this.currentValue = value;
},
}
<!– 3. 重要:通过 watch 父组件的 value 改变才会更新子组件 这里之前就会出现父级 value 改变 子代不改变的 bug–>
watch: {
value(val, oldValue) {
this.setCurrentValue(val);
}
},
2. 父子组件的 DOM 事件
// 例子
<input type=”checkbox” ref=’switch’ @click=”handleClick”/>
handleClick(evt) {
this.$emit(‘click’, evt);
}
// 例子
<input type=”checkbox” ref=’switch’ @focus=”handleFocus”/>
handleFocus(event) {
this.focused = true;
this.$emit(‘focus’, event);
},
3. 父子组件的 data
比如 placeholder 可以直接写,在组件中使用 v -bind:’$attrs’
4. 父子组件的方法

组件使用的时候会触发一些方法,这个方法怎么在父级执行,比如登陆按钮触发(19/4/8: 回头看看这句话,我当时在想什么?)
这个应该看做上面 DOM 事件,当子元素触发 click,onmouseout,onmouseenter 时通过 emit 触发父级的一个方法

5. 父子组件样式的设置
/* 父组件 */
<Example :width=40></Example>
/* 子组件 */
<span class=”el-switch__core” ref=”core” :style=”{‘width’: width + ‘px’}”>
6. 组件的注册和安装
Vue.use()
// 第一步,注册组件
import Switch from ‘./src/component’;
Switch.install = function(Vue) {
// name=”TuiSwitch”
Vue.component(Switch.name, Switch);
};
export default Switch;
// 第二步,安装组件(main.js)
import Switch from ‘install 文件 ’;
Vue.use(Switch);
// 第三步,在组件中直接使用即可
<tui-switch></tui-switch>
7. 百分比宽高

脱离标准文档流的块级元素的百分百宽高是根据距离最近的定位元素的 borderwidth
一般情况下设置百分百自适应布局是根据父级元素的 contentwidth

8. 垂直方向 margin,padding 的百分比

垂直方向 margin,padding 百分比,往往不是我们意料中是根据上级元素的高度 (height) 来计算的,其实是根据宽度 (width) 来计算的
如果为了实现间距效果可以使用一个空的且高度 (height) 为百分比的 div 来实现

9.axios
(19/4/8: 今天修改到这里)
全局配置:
axios.defaults.timeout = 30000;
// 开发时只关心接口名称即可
axios.defaults.baseURL = “http://10.0.1.1:8080”;
// 配置 axios 的拦截器,可以用来配置 token
axios.interceptors.request.use(
config => {
if (localStorage.getItem(“token”)) {
config.headers.Authorization = localStorage.getItem(“token”);
}
return config
},
error => {
return Promise.reject(error)
}
);
axios 出现 415 错误, 需要在 request 设置 content-type
axios
.post(url, params, {
headers: {
“Content-Type”: “application/json;charset=UTF-8”
}
})
.then(res=>{})
生成有转义字符的数据
JSON.stringify({sdkParams: JSON.stringify(this.params)});
<!– 这样子后面的字符串中的引号就会有转义字符 –>
10.git

origin master:master
通过 git remote -v 显示 origin 代表的远程地址
第一个 master 表示本地的分支名
第二个 master 表示远程的分支名, 如果没有会新建
win10 中使用 webstorm 时 git 错了会保存错误的密码用户名 push 会一直出错 可以搜索 windows 凭据 然后修改即可

11. 分析 elementUI

解压压缩包到当前项目的 node_modules 目录
node modules 的一些规则
import element-ui from ‘element-ui’

Node 将在 node_modules 中搜索 element-ui 目录,Node 会假设 element-ui 为一个包并试图找到包定义文件 package.json。如果 element-uir 目录里没有包含 package.json 文件,Node 会假设默认主文件为 index.js,即会加载 index.js。如果 index.js 也不存在,那么加载将失败。
package.json:
{
“license”: “MIT”,
“main”: “lib/element-ui.common.js”,
“name”: “element-ui”,
}
node 将会返回 element-ui.common.js 模块
12.WebSocket
websocket(prodId) {
this.ws = new WebSocket(‘ws://……….’);
this.ws.onopen = function () {
console.log(“ws connected!”);
};
this.ws.onmessage = e => {

};
this.ws.onclose = function () {
// 关闭 websocket 后的回调函数
console.log(“ws closed”);
};
// 组件销毁时调用,主动关闭 websocket 连接
this.over = () => {
ws.close();
};
},
beforeDestroy() {
if (this.over) {
this.over();
}
send(msg){
if(this.ws){
this.ws.send(msg)
}
}
13. 不喜欢 Vue 单文件?
<!– 这是文件扩展名为 .vue 的 single-file components(单文件组件) 相比传统 new Vue({el: ‘#container ‘})要好很多 –>
<template>
<div>This will be pre-compiled</div>
</template>
<script src=”./my-component.js”></script>
<style src=”./my-component.css”></style>
14. 动画
具有切换效果的动画可以使用 checked 来实现,并且可以使用 $refs 来根据 checked 的状态来设置样式
15.vh,vw
相对于视口的长度单位,pc 视口就是浏览器窗口,所以可以实现 window.onresize 的效果
16.flex

flex 是 flex-grow flex-shrink flex-basis 的缩写

flex-grow 相比其他元素的比例
flex-shrink 元素占得份数,用来计算收缩时缩放的程度
flex-basis 元素默认宽度

flex:1 的子代会自动撑开填满空白部位, 在不使用绝对定位时,可以实现一个宽度自适应,另一个宽度固定(使用 width 设置固定值)

在有高的容器中实现标题在上面, 内容在剩余高度居中:

父元素 `display: flex;flex-direction: row;flex-wrap: wrap;`
标题 flex:1 来实现占满一行
需要居中的子元素 align-self: flex-start||baseline; 都可以实现,(测试元素是一个 img)

17. 事件的解绑
var EventUtil={
/* 检测绑定事件 */
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}
else if(element.attachEvent){
element.attachEvent(‘on’+type,handler);
}
else{
element[“on”+type]=handler /* 直接赋给事件 */
}

},
/* 通过 removeHandler*/
removeHandler:function(element,type,handler) {/*Chrome*/
if (element.removeEventListener)
element.removeEventListener(type, handler, false);
else if (element.deattachEvent) {/*IE*/
element.deattachEvent(‘on’ + type, handler);
}
else {
element[“on” + type] = null;
/* 直接赋给事件 */
}
}
};
### 18. 多层组件的封装

其实多层组件中参数是可以传递的, 比如这个 taginput, 而且使用 v -model 传递参数,并不需要使用 watch 监听 value 改变然后使用 $emit,value 变化会自动从子组件传递到最外层

18-9-13 当时的观点有点弱智啊,这个这个直接改变了子组件中的值啊,可能是因为对象深度原因没有报错?
<!– 数据单向绑定,不需要使用 watch 从父组件更新值到子组件,这句话很矛盾啊 –>
<template>
<div>
<div class=”product_info_row” v-for='(arr,index) in value’ :key=”index”>
<div class=”tag_input”>
<lubun-input :cancleable=’true’ v-for='(v,i) in arr.tags’ :key=”i” type=”text” v-model=”v.value” :width=width :tagrow=’index’
:tagid=’v.id’ @click=’removeTag’ />
<!– 失去焦点也需要保存 –>
<lubun-input autofocus v-if=’arr.show’ type=”text” v-model=”arr.value” :width=80 @enter=’buildTag(arr)’ @blur=’buildTag(arr)’
/>
<lubun-button v-else text=”+” @click=”addTag(arr)” :width=30 />
</div>
</div>
</div>
</template>
<script>
export default {
name: “LubunTaginput”,
componentName: “LubunTaginput”,
props: {
width: {
type: [String, Number],
default: 150
},
value: {
type: Array,
default: function () {
return [
{
id: 0,
show: false,
value: “”,
tags: []
}
]
}
}
},
methods: {
addTag(rows) {
rows.show = true;
rows.value = “”;
},
removeTag(obj) {
// console.log(“ 传入的 rows:”,obj);
var tags = this.value[obj.tagRow].tags;
// console.log(“rows 中的 tags”,tags);
for (let i = 0; i < tags.length; i++) {
// console.log(“ 循环 tags:”, tags[i]);
if (tags[i].id === obj.tagId) {
// console.log(‘find’);
tags.splice(i, 1);
return;
}
}
},
buildTag(rows) {
rows.show = false;
// 内容为空不会生成 tag
if (rows.value !== “”) {
rows.tags.push({id: rows.id++, value: rows.value});
rows.value = “”;
}
}
},
};
</script>

19.absolute 的宽度

有些情况 absolute 居中需要使用两个嵌套 absolute 的 div,这个时候最外层有可能会宽度不会被内容撑开,如果是文本的话可以使用 white-space:nowrap 这样就可以撑开两个 div 了
transform: translateX(-50%);transform: translateY(-50%); 只会有一个生效,需要使用缩写形式 transform: translate(-50%,-50%); 这个可以实现一个 absolute 块完美居中

20.vue 事件总线:BUS

解决组件之间信息的传递, 发送数据的组件使用 $on(“Event Name”,value)事件触发 (当数据改变时触发这个函数就可以了), 接受数据的组件使用 $emit(“Event Name”,callback) 接受即可, 你可以把这个事件注册在 mounted 中(之后这个就可以等着其他组件 $emit 的数据了)
事件有时候会重复触发:注册的事件是全局的,它并不会随着组件的销毁而自动注销,需要手动注销

beforeDestroy() {
// 组件销毁前需要解绑事件。否则会出现重复触发事件的问题
bus.$off(“Event Name”);
},
PS: 在生命周期 beforeDestroy,destroy 中,this.$route.path 获取到的其实是下一个页面的 path
21.flex 两列布局

左右两列布局
左边宽度自适应,但是有个最小宽度
右边宽度固定

father{
display: flex;
width: 100%;
height: 100%;
overflow: auto;
}
child-left{
position: relative;
min-width: 800px;
height: 100%;
flex: 1;
}
child-right{
position: relative;
min-width: 420px;
height: 100%;
}
22. 上传图片按钮
使用 lable+vue 实现
<label style=”width:80%” class=’btn’ for=”uploadPic”> 上传文件 </label>
<input id=”uploadPic” type=”file” @change=’uploadPic’ style=”display:none”>
使用 js 实现
<span class=”button” onclick=”sel_local_images()” id=”add_img” title=”Add new file from local disk”> 加载图片 </span>
<input type=”file” id=”invisible_file_input” name=”files[]” style=”display:none”>
//js 中再给 input 加 change 事件即可
// 这个也可以在 vue 中使用,给 input 加 @change, 然后手动触发这个 input 的 click()就可以弹出上传文件的界面了
var invisible_file_input = document.getElementById(“invisible_file_input”);
function sel_local_images() {
if (invisible_file_input) {
invisible_file_input.setAttribute(‘multiple’, ‘multiple’);
invisible_file_input.accept = ‘.jpg,.jpeg,.png,.bmp’;
invisible_file_input.onchange = (e)=>{
// 获得第一张图片
var img = e.target.files[0];
};
invisible_file_input.click();
}
}
23. 图片 base64 显示
//reader 有几个读取文件的方法用于读取成不同格式,还有几个钩子函数用于不同阶段执行一些功能
// var img =xxxxxxxxxxxx
// 将图片的 base64 格式显示在页面上, 图片可以使用 input 上传
let reader = new FileReader();
reader.onload = (theFile)=>{
that.imgLoaded = e.target.result;
// 2018-10-24 图片还没有加载完成, 不是 DOM 的问题, 使用 onload 才能正确获得原始宽高
var i = new Image();
i.src = e.target.result;
i.onload = () => {
img_loaded_width = i.width;
img_loaded_height = i.height;
}}
// 这个貌似会执行上面的 onload
reader.readAsDataURL(img);
24. 前端下载文件
var blob = new Blob([JSON.stringify(data, null, 2)],{type : ‘application/json’});
var aTag = document.getElementById(‘download_img’);
aTag.setAttribute(‘href’, URL.createObjectURL(blob));
aTag.setAttribute(‘download’, that.imgLoadedName.split(‘.’)[0]+’.json’);
aTag.click();
25. vue 中 ref 命名不可以使用中划线
26. 前端调用相机

通过 video 控件,通过捕获 video 的流,截取 video 中的图像实现拍照,
通过 input[file]控件调用移动端的摄像头,实现拍照。

第一种可以实现对拍照界面的重写,IPhone 环境 11 开始兼容;

第二种方式实际上是调用 input[type=’file’],会弹出一个选择框让用户选择是调用相机还是调用相册,

第二种兼容优于第一种,不好的地方就是这种方法无法控制拍照,想要在移动端实现只能拍照不能选择照片或者在拍照界面添加引导遮罩层的方法是行不通了。

<video width=”640″ height=”480″ id=”myVideo”></video>
<canvas width=”640″ height=”480″ id=”myCanvas”></canvas>
<button id=”myButton”> 截图 </button>
<button id=”myButton2″> 预览 </button>
<button id=”myButton3″>
<a download=”video.png”> 另存为 </a>
</button>

var cobj = document.getElementById(‘myCanvas’).getContext(‘2d’);
var vobj = document.getElementById(‘myVideo’);

getUserMedia({video: true}, function (stream) {
vobj.src = stream;
vobj.play();
}, function () {
});

document.getElementById(‘myButton’).addEventListener(‘click’, function () {
cobj.drawImage(vobj, 0, 0, 640, 480);
document.getElementById(‘myButton3’).children[0].href = cobj.canvas.toDataURL(“image/png”);
}, false);

document.getElementById(‘myButton2’).addEventListener(‘click’, function () {
window.open(cobj.canvas.toDataURL(“image/png”), ‘_blank’);
}, false);

function getUserMedia(obj, success, error) {
if (navigator.getUserMedia) {
getUserMedia = function (obj, success, error) {
navigator.getUserMedia(obj, function (stream) {
success(stream);
}, error);
}
} else if (navigator.webkitGetUserMedia) {
getUserMedia = function (obj, success, error) {
navigator.webkitGetUserMedia(obj, function (stream) {
var _URL = window.URL || window.webkitURL;
success(_URL.createObjectURL(stream));
}, error);
}
} else if (navigator.mozGetUserMedia) {
getUserMedia = function (obj, success, error) {
navigator.mozGetUserMedia(obj, function (stream) {
success(window.URL.createObjectURL(stream));
}, error);
}
} else {
return false;
}
return getUserMedia(obj, success, error);
}

27. xml 解析:<command> 标签在转成 DOM 时会直接去掉中间的子代,很奇怪
28. el 的表单验证 prop 和 rule 中属性名和 v -model 的参数名称是要相同的
29. RestTemplate 的请求会通过其他服务的过滤器
30. jenkins rancher 实现持续集成 jira 跟踪
31. n 不换行
有时候后端传过来的字符串中 \\n 转成字符对象时会成为 \n 但是在 vue 中渲染到页面中不会出现换行,而是显示为 \n
(compose.environment[key]).replace(/\\n/g,’\n’) 然后就可以实现前端换行了 很奇怪 不知道为什么
32. 传递单纯数组的后端操作
//{men:{name:’Kik’,age:11},place:[‘A’,’B’,’C’]}
ArrayList<String> place = (ArrayList<String>) jsonObject.getObject(“place”, List.class);
33. fastJSON 一般用法
String algorithmList = “[“a”,”b”,”c”]”
List<String> apacMainIdList = JSONObject.parseObject(algorithmList, List.class);
34. body 的带 siderbar 的自适应布局
<!– 使用 absolute 和 top,left,right,bottom 来完成 div 填充剩余部分 –>
<div style=”position:absolute;background:#ccc;top:0;left:0;right:0;bottom:0;overflow:hidden”>
<div style=”position:absolute;width:140px;background:#C00;top:0;left:0;bottom:0″>
</div>
<div style=”position:absolute;left:140px;background:#0C0;top:0;right:0;bottom:0;”>
</div>
</div>
35. 前端读取网络路径文件
XMLHttpRequest
// 读取为二进制
function createXHR(){
return window.XMLHttpRequest?
new XMLHttpRequest():
new ActiveXObject(“Microsoft.XMLHTTP”);
}
function getData(url){
let request = createXHR();
request.open(“GET”,url,false);
request.responseType = ‘blob’;
request.onload = function () {
var reader = new FileReader();
reader.readAsArrayBuffer(request.response);
reader.onload = function (e) {
var DAT_data = e.target.result;
console.log(“DAT_data:” + DAT_data);
};
};
request.send();
}
ajax
可以参考
组件中传递参数等方法:https://segmentfault.com/a/11…

退出移动版