Table of Contents
下面回顾一下练习项目——爱家——过程中碰到的问题。因为是回顾,所以可能不尽全面。不过,尽力了,能写多少是多少。
一、轮播图兼容问题
因为 ie9 不支持transform
,所以引入了jQuery
,使用jQuery
的animate
实现无限轮播。
<template> <div id="banner" @mouseenter = "stop" @mouseleave = "start" > <div class="pic"> <ul> <li v-for="(img,index) in imgArray" :key = "index" class = "fl" > <a> <img :src="img" alt="" class="bgc"> </a> </li> </ul> </div> <div class="dot"> <span v-for="index in dotArray" :key = "index" @click = "slide(index)" :class="{active:index === curIndex}" ></span> </div> <div class="left" @click="slide('l')"><</div> <div class="right" @click="slide('r')">></div> </div> </template> <script> import $ from "jquery"; export default { props: { banner: { type: Object, required: true } }, data(){ return { imgArray: [ this.banner['2'].src, this.banner['0'].src, this.banner['1'].src, this.banner['2'].src, this.banner['0'].src ], dotArray: [ 1, 2, 3 ], curIndex: 1 } }, methods: { slide:function(index){ var $pic = $('.pic').first(); var self = this; if($pic.is(":animated")){ return } index = 'l' === index ? this.curIndex - 1 : index; index = 'r' === index ? this.curIndex + 1 : index; this.curIndex = index; var left = this.curIndex; if(this.curIndex >= this.imgArray.length - 1){ this.curIndex = 1; } if(this.curIndex <= 0){ this.curIndex = this.imgArray.length - 2; } $pic.animate({ 'margin-left': 0 - 1200 * left + 'px' },1000,function(){ if(index >= self.imgArray.length - 1){ $pic.css({ 'margin-left': '-1200px' }) }; if(index <= 0){ $pic.css({ 'margin-left': '-3600px' }) } }); }, start: function(){ var self = this; // console.log(self); self.interval = setInterval(function(){ // console.log(self); self.curIndex++; // if(self.curIndex === self.imgArray.length){ // self.curIndex = 0; // } self.slide(self.curIndex); },2000) }, stop: function(){ clearInterval(this.interval); } }, mounted: function(){ this.start(); } } </script> <style scoped> #banner { position: relative; width: 1200px; height: 430px; overflow: hidden; margin: auto; } .pic { overflow: hidden; width: 6000px; margin-left: -1200px; } .pic img { width: 1200px; height: 430px; } .dot { text-align: center; margin-top: -50px; } .dot span { width: 20px; height: 20px; border-radius: 50%; margin: 0 10px; display: inline-block; background-color: #fff; border: 2px solid #fff; cursor: pointer; } .dot span.active { background-color: #333; border: 2px solid linen; } .left, .right { position: absolute; top:50%; margin-top: -50px; height: 100px; width: 30px; line-height: 100px; text-align: center; background-color: #333; color: #fff; cursor: pointer; } .right { right: 0px; } </style>
二、依据条件渲染链接
图片等都是使用vue
的for
循环渲染的。在实际展示的时候有一个需求:如果内容存在,则链接有效。否则使用默认内容,链接无效。
<a :href="'#' === a_case.id ? 'javascript:;' : './?c_id=' + a_case.id" :target="'#' !== a_case.id ? '_blank' : ''" ><img :src="a_case.cover" :alt="a_case.title" class="cover" ></a><br /> <a :href="'#' === a_case.id ? 'javascript:;' : './?c_id=' + a_case.id" :target="'#' !== a_case.id ? '_blank' : ''" ><span v-html="a_case.title" class="ellipse ilb" ></span></a>
#
是在id
不存在的时候约定的默认符号。如果id
不存在,渲染时候的默认链接就是javasript:;
。
默认链接不能是#
。如果是锚点,页面会跳到头部,在target
为_blank
的时候还会在新页面打开。
默认链接不能是javascript:void(0);
。主要是在 IE 下面会出现问题。(好像是打开新标签。)
三、设计师照片条件渲染
这个问题是由前期设计不足引起的。在之前设计的时候并没有考虑到缩略图的问题。比如设计师,上传的时候是大图片。但一般页面需要的是缩略图。大图太耗带宽了。所以,只能在后台增加了裁切的代码。但是已经注册并上传图片的设计师并没有对应的缩略图——除非再一次上传图片。为了在前端兼容这种情况,需要条件渲染。
<img :src="designer.thumb ? designer.thumb : designer.photo" :alt="designer.name" class="photo" />
如果缩略图存在,就使用缩略图,否则用原图。
四、年龄计算
出生年月和工作年月在数据库中是这样存储的:XXXX_XX
。比如1980_6
。但在展示的时候需要以这种格式展示:XX岁
和XX年XX月
。所以,就需要对原数据进行转换。
computed: { age(){ return Math.floor(new Date().getFullYear() - this.user.birth.split('_')[0] + (new Date().getMonth() - this.user.birth.split('_')[1])/12) + '岁'; }, working(){ return Math.floor(new Date().getFullYear() - this.designer.working_years.split('_')[0] + ( new Date().getMonth() - this.designer.working_years.split('_')[1] ) /12 ) + '年' + (new Date().getMonth() - this.designer.working_years.split('_')[1] + 12)%12 + '月'; } }
this.user.birth
是出生年月, this.designer.working_years
是参加工作年月。
五、div 垂直居中问题
有一个需求,在一个盒子中显示联系方式。盒子宽高确定,联系方式条数不确定。
@white: white; @pink: #edd9d9; .contact-board { width: 240px; height: 200px; line-height: 200px; background: @white; border: 5px solid @pink; left: 50%; top: 50%; margin-left: -120px; margin-top: -100px; display: table; > div:first-child { display: table-cell; vertical-align: middle; } p { line-height: 35px; }
这里用的less
。外层盒子.contact-board
的display
属性为table
。内层盒子> div:first-child
的display
属性为table-cell
,vertical-align
属性为middle
。
六、scoped 穿透
通用组件确实很方便,但不同的页面布局有所不同,有必要对通用组件进行改造。
还是放参考链接吧。# Vue中的scoped及穿透方法
在练习中我采用了第二种方法:曲线救国。
七、vue-router 默认路由
const routes = [ { path: '/', redirect: '/login' }, { path: '/login', component: login }, { path: '/register', component: register } ];
redirect
后面可以是一个函数,根据需要返回不同的路由。
八、通过实例方法改变当前路由
还是先放链接:## vue router.push(),router.replace(),router.go()
this.$router.replace({path:'/setting'});
九、vue-router 传值方法
路由传值有params
、query
、props
。
先放链接:Vue-router props 如何传递参数 ,传参请看这里
路由里面props
的值确实可以传给子组件的props
。
我的需求是,父组件向后台发出ajax
请求,返回的一堆数据在分解后需要传给子组件。路由的props
属性貌似无法取得父组件的数据。虽然可以在父组件中通过this.$route……
这样的方式一层一层地找到需要数据的子组件,然后赋值,但这样并不是响应式的。后来,只能通过vuex
解决问题。父组件将数据存入vuex
。
this.$store.commit('store_origin_info',this.info);
子组件监听变化。
created(){ this.$store.watch(state => { if(!state.origin_info){ return; } this.origin_info = this.$store.state.origin_info; this.init_info(); }); }
一开始并不是用的监听的方式,而是在computed
中返回。
computed: { origin_info: function(){ return this.$store.state.origin_info; } },
但不知为什么,这种无法响应store
中数据的变化。
十、添加图片并预览的一系列问题
这里的问题比较多,主要是 ie9 的兼容。
input
不能连续选择同一张图片。- ie9 不支持
type=file
,无法通过e.target.files
获取文件信息。 - ie9 不支持
FileReader
,无法通过此方法将图片读取为base64
用于预览。
先放代码。
add(e,item,index){ e = e || event; //如果值为空,返回。主要针对 createTextRange 触发的onchange if(!e.target.value){ return; } let pics = []; if(window.FileReader){ pics = e.target.files; }else{ //低版本ie兼容 try{ e.target.select(); e.target.blur(); let path = document.selection.createRange().text; let fso = new ActiveXObject("Scripting.FileSystemObject"); pics[0] = {}; pics[0].path = path; pics[0].size = fso.GetFile(path).size; pics[0].type = fso.GetFile(path).type; }catch(e){ alert(e+"\n"+"如果错误为:Error:Automation 服务器不能创建对象;"+"\n"+"请按以下方法配置浏览器:"+"\n"+"请打开【Internet选项-安全-Internet-自定义级别-ActiveX控件和插件-对未标记为可安全执行脚本的ActiveX控件初始化并执行脚本(不安全)-点击启用-确定】"); } } let len = pics.length; //这里的mark 为什么设置为 -2,而不是0.原因未知,总之下面的 mark++ 执行次数会比循环次数多 2。所以设置为 -2 . let mark = -2; if(!window.FileReader){ //ie9 不存在多执行两次的情况 mark = 0; } // console.log(e.target); // console.log(pics); let rule = { size: 300 * 1024, type: [ 'jpeg', 'png', 'gif' ] }; for(let key in pics){ this._CHECK_PIC(rule,pics[key]) .then( result => { if(result.right){ console.log("359" + pics); item.detail.push({ path: result.data, description: '' }); } mark++; console.log('len:'+len+' mark:'+mark); if(len === mark){ e.target.value = ''; if(window.ActiveXObject){ e.target.createTextRange().execCommand('delete'); e.target.blur(); } console.log(e.target.value); this.check_content(index); } }); } }
add()
是input onchange
事件的处理函数(方法)。参数分别是事件对象、要添加图片的数组和索引。_CHECK_PIC()
是封装的图片检查方法。check_content()
是整个内容区域的检查方法。
下面是封装的_CHECK_PIC()
Vue.prototype._CHECK_PIC = function(rules,pic){ //预设变量 let variable = rules; let result = { right: true, data: null }; return new Promise(function(resolve,reject){ //检查图片格式 if(rules.type){ let r = rules.type.some(val => { if(window.FileReader){ return pic.type === 'image/' + val; }else{ //低版本ie兼容 console.log(pic); let type = pic.type.split(" ")[0].toLowerCase(); if('jpg' === type){ type = 'jpeg'; } return val === type; } }); if(!r){ result.right = false; resolve(result); return; } } //检查图片大小 if(rules.size){ if(pic.size > rules.size){ result.right = false; resolve(result); return; } } //检查图片宽高 if(window.FileReader){ let reader = new FileReader(); //读取图片 reader.readAsDataURL(pic); reader.onload = function(e){ let temp_pic = new Image(); temp_pic.src = e.target.result; temp_pic.onload = function(){ // console.log(temp_pic.width); // console.log(temp_pic.height); // console.log(variable); check_w_h(temp_pic,rules,result); } } }else{ //低版本ie兼容 let temp_pic = new Image(); temp_pic.src = pic.path; temp_pic.onload = function(){ // console.log("89" + this); check_w_h(temp_pic,rules,result); } } //检查宽高 function check_w_h(temp_pic,rules,result){ // console.log("98" + this); //检查最小宽高 if(rules.min_width && rules.min_height){ if(temp_pic.width < rules.min_width || temp_pic.height < rules.min_height){ result.right = false; resolve(result); return; } } //检查最大宽高 if(rules.max_width && rules.max_height){ if(temp_pic.width > rules.max_width || temp_pic.height > rules.max_height){ result.right = false; resolve(result); return; } } //检查最小宽高比 if(rules.min_ratio){ if(temp_pic.width / temp_pic.height < rules.min_ratio * 0.9){ result.right = false; resolve(result); return; } } //检查最大宽高比 if(rules.max_ratio){ if(temp_pic.width / temp_pic.height > rules.max_ratio * 1.1){ result.right = false; resolve(result); return; } } //图片有效 result.data = temp_pic.src; // console.log(result); resolve(result); } }); }
input
不能连续选择同一张图片可以通过清空input value
值来解决。ie11 及非 ie 浏览器可以通过e.target.value=''
实现。其余 ie 通过e.target.createTextRange().execCommand('delete'); e.target.blur();
解决。但这样貌似会触发onchange
事件,导致一次添加两张图的bug。所以,在前面要对input value
进行判断。if(!e.target.value){return;}
。
ie9 不支持type=file
和FileReader
可以通过ActiveXObject("Scripting.FileSyetemObject")
和滤镜解决。下面放链接:
上传图片快速预览HTML5 FileReader + Window.URL+滤镜(兼容低版本IE)
不知为什么,好像不使用滤镜,直接将路径给图片的src
属性也可以。
ie9 下将图片转为 base64 的问题尚未解决。
十一、axios post 请求后台接收不到数据
先放链接:
解决方法比较多,我这里是通过transformRequest
解决的。
Vue.prototype._POST = function(post_data,func){ axios({ method: 'post', url: './', headers: { 'Content-type': 'application/x-www-form-urlencoded' }, data: post_data, transformRequest: [ function(data){ let newData = ''; for ( let i in data){ newData += encodeURIComponent(i) + '=' + encodeURIComponent(data[i]) + '&'; } // console.log(newData); return newData; } ] }).then(response => { func(response); }); }
十二、数据挂载
目前是将数据挂载在vuebody
下面。或许可以考虑将数据挂载到meta
标签下面,或者弄一个其他不需要的标签,省去读取数据后在将vuebody
设为显示的步骤。
十三、webpack css 分离问题
之前写诗词网站demo的时候就尝试过将css分离出来,但失败了。这一次倒是成功了。原因嘛——之前配置写错了。
const webpack = require('webpack'); const VueLoadPlugin = require('vue-loader/lib/plugin'); const path = require('path'); const HTMLPlugin = require('html-webpack-plugin'); const miniCSS = require('mini-css-extract-plugin'); const isDev = process.env.NODE_ENV === 'development'; const config = { mode: isDev ? 'development' : 'production', target: 'web', entry: { home: './src/pages/home/home.js', designer: './src/pages/designer/designer.js', about: './src/pages/about/about.js', detail: './src/pages/detail/detail.js', login: './src/pages/login/login.js', panel: './src/pages/panel/panel.js' }, output: { path: path.resolve(__dirname,'dist'), filename: 'js/[name].js' }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.css$/, use: [ miniCSS.loader, 'css-loader' ] }, { test: /\.less$/, use: [ miniCSS.loader, 'css-loader', 'less-loader' ] }, { test: /iconfont\.(ttf|svg|eot|woff)$/, use: [{ loader: 'file-loader', options: { name: 'font/[name].[ext]' } }] }, { test: /\.js$/, use: { loader: 'babel-loader' }, exclude: path.resolve(__dirname,'node_modules/') } ] }, plugins: [ new VueLoadPlugin(), new miniCSS({ filename: "css/[name].css", allChunks: true }), new HTMLPlugin({ template: './src/pages/home/home.html', filename: './home.html', chunks: ['lib','home'] }), new HTMLPlugin({ template: './src/pages/about/about.html', filename: './about.html', chunks: ['lib','about'] }), new HTMLPlugin({ template: './src/pages/login/login.html', filename: './login.html', chunks: ['lib','login'] }), new HTMLPlugin({ template: './src/pages/detail/detail.html', filename: './detail.html', chunks: ['lib','detail'] }), new HTMLPlugin({ template: './src/pages/designer/designer.html', filename: './designer.html', chunks: ['lib','designer'] }), new HTMLPlugin({ template: './src/pages/panel/panel.html', filename: './panel.html', chunks: ['lib','panel'] }) ], optimization: { splitChunks: { cacheGroups: { lib: { name: 'lib', filename: 'js/[name].js', minChunks: 2, chunks: 'initial' } } } } }; module.exports = config;
只要将module
里面css
和less
部分的style-loader
换成mini-css-extract-plugin
的loader
就可以了。当然,plugins
里面也要配置一下就是了。(之前optimization
里面也写了,并且style-loader
没删掉。)
十四、php 部分
实际上这里也有很多东西可以写。但是,还是直接放源码链接好了。
https://github.com/tonyzhou1890/love-home/blob/master/src/index.php
总结
数据库部分也就不记录了。
1、总体来说,还很弱。
2、总体来说,还有很大进步空间。
文章最初发布在简书,时间为 2018.07.14 18:08。