练习的是一个简单的前端页面,看着视频抄的,仿去哪儿的手机端,其中包含了git的使用、vue的基础点、一些插件的应用,在这里记录一下。
项目练习的源码地址: https://github.com/SeriousWatermelon/Travel.git
目录
1. 项目环境准备
1.1 安装node.js运行环境: https://nodejs.org/en/download/
1.2 git的基础命令(gitbash中运行)
1.3 创建VUE项目(cmd中运行命令)
1.4 vue项目文件目录说明
2. VUE项目开发零散知识点
2.1 多页面应用与单页面应用
2.2 标签
2.3 移动端网页注意项
2.4 iconfont的使用
2.5 stylus格式的css使用
2.6 px和rem单位转换
2.7 Vue配置CSS的全局变量
2.8 vue中的文件路径
2.9 vue中使用ajax请求数据
2.10 Vuex数据层框架
2.11 Vuex的高级使用localstorage
2.12 带参数的路由跳转
2.13 通过ip访问vue项目
2.14 tomcat的简单部署
vue官网: https://cn.vuejs.org/v2/guide/installation.html
|—projectName目录
|—build目录 项目打包的配置内容
|—webpack.base.conf.js 基础webpack配置项
|—webpack.dev.conf.js 开发webpack配置项
|—webpack.prod.conf.js 线上webpack配置项
|—config目录 项目配置文件
|—dev.env.js 开发环境配置信息
|—index.js 基础配置信息
|—prod.evn.js 线上环境配置信息
|—node_modules目录 项目依赖的三方node包,不必管理
|—src目录 整个项目的源代码目录
|—assets 项目图片类资源
|—components目录 项目小组件
|—router目录 配置项目路由
|—App.vue文件 原始根组件
|—main.js文件 项目入口文件
|—static目录 存放静态资源的目录(图片、模拟的json数据等)目录
|—.babelrc文件 vue单文件组件需要使用babelrc进行语法解析,进行转换,使浏览器可以编译执行
|—.editorconfig文件 配置编辑器的语法
|—.eslintignore文件 配置不受eslint代码规范检测工具检测的目录文件
|—.eslintrc.js文件 vue代码规范
|—.gitIgnore文件 配置git托管需要忽略的文件
|—.postcssrc.js文件 postcss的配置项
|—index.html文件 默认首页模板文件
|—LICENE文件 开源协议说明
|—package-lock.json文件 package的锁文件,确定安装三方依赖的具体版本
|—package.json文件 项目三方依赖,可以设置项目的ip方式访问(修改script结点中的dev,添加 --host 0.0.0.0 )
|—README.md文件 项目说明文件
多页面应用:每一次页面跳转,都会使用新的HTML;优点:首屏时间快(页面收个内容展示出的时间;用户请求->服务器返回url,只经历了一个http请求);SEO(搜索引擎)效果优化好。缺点:页面切换较慢。
单页面应用:每一次跳转都是js动态删除当前的页面,再渲染下一个页面。优点:页面切换快。缺点:首屏时间稍慢,SEO差。
跳转标签,类似于html5的a标签
补充:编程式导航
在js事件处理完毕后,若需要跳转到其他页面,vue中我们可以使用路由跳转 this.$router.push('/home')。类似于传统的location.href。
1. 禁止双指放大或缩小网页,需要在最外层的index.html中,添加如下设置
2. 不同手机浏览器浏览网站的默认样式不统一,需要设置统一。这里通过引入reset.css解决,存放目录(/src/assets/styles/reset.css)。只需引入即可。
vue项目引入该css文件:在main.js文件(项目入口文件)中引入:import './assets/styles/reset.css'
/*reset.css文件*/
@charset "utf-8";html{background-color:#fff;color:#000;font-size:12px}
body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0}
body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%}
h1,h2,h3,h4,h5,h6{font-family:tahoma,arial,"Hiragino Sans GB","微软雅黑",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,b,strong{font-weight:normal}
address,cite,dfn,em,i,optgroup,var{font-style:normal}
table{border-collapse:collapse;border-spacing:0;text-align:left}
caption,th{text-align:inherit}
ul,ol,menu{list-style:none}
fieldset,img{border:0}
img,object,input,textarea,button,select{vertical-align:middle}
article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
blockquote:before,blockquote:after,q:before,q:after{content:"\0020"}
textarea{overflow:auto;resize:vertical}
input,textarea,button,select,a{outline:0 none;border: none;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
mark{background-color:transparent}
a,ins,s,u,del{text-decoration:none}
sup,sub{vertical-align:baseline}
html {overflow-x: hidden;height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;}
body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #333;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;}
hr {height: .02rem;margin: .1rem 0;border: medium none;border-top: .02rem solid #cacaca;}
a {color: #25a4bb;text-decoration: none;}
3. 解决移动端1px边框的问题(手机分辨率不同而产生的)。这里通过引入border.css文件解决,存放目录(/src/assets/styles/border.css)。根据页面样式需求使用border.css封装好的样式。
vue项目引入该css文件:在main.js文件(项目入口文件)中引入:import './assets/styles/border.css'
/*border.css文件*/
@charset "utf-8";
.border,
.border-top,
.border-right,
.border-bottom,
.border-left,
.border-topbottom,
.border-rightleft,
.border-topleft,
.border-rightbottom,
.border-topright,
.border-bottomleft {
position: relative;
}
.border::before,
.border-top::before,
.border-right::before,
.border-bottom::before,
.border-left::before,
.border-topbottom::before,
.border-topbottom::after,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::before,
.border-topleft::after,
.border-rightbottom::before,
.border-rightbottom::after,
.border-topright::before,
.border-topright::after,
.border-bottomleft::before,
.border-bottomleft::after {
content: "\0020";
overflow: hidden;
position: absolute;
}
/* border
* 因,边框是由伪元素区域遮盖在父级
* 故,子级若有交互,需要对子级设置
* 定位 及 z轴
*/
.border::before {
box-sizing: border-box;
top: 0;
left: 0;
height: 100%;
width: 100%;
border: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
left: 0;
width: 100%;
height: 1px;
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
top: 0;
width: 1px;
height: 100%;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
border-top: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-right::before,
.border-rightbottom::before,
.border-rightleft::before,
.border-topright::after {
border-right: 1px solid #eaeaea;
transform-origin: 100% 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::before {
border-bottom: 1px solid #eaeaea;
transform-origin: 0 100%;
}
.border-left::before,
.border-topleft::after,
.border-rightleft::after,
.border-bottomleft::after {
border-left: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
top: 0;
}
.border-right::before,
.border-rightleft::after,
.border-rightbottom::before,
.border-topright::after {
right: 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::after {
bottom: 0;
}
.border-left::before,
.border-rightleft::before,
.border-topleft::after,
.border-bottomleft::before {
left: 0;
}
@media (max--moz-device-pixel-ratio: 1.49), (-webkit-max-device-pixel-ratio: 1.49), (max-device-pixel-ratio: 1.49), (max-resolution: 143dpi), (max-resolution: 1.49dppx) {
/* 默认值,无需重置 */
}
@media (min--moz-device-pixel-ratio: 1.5) and (max--moz-device-pixel-ratio: 2.49), (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 2.49), (min-device-pixel-ratio: 1.5) and (max-device-pixel-ratio: 2.49), (min-resolution: 144dpi) and (max-resolution: 239dpi), (min-resolution: 1.5dppx) and (max-resolution: 2.49dppx) {
.border::before {
width: 200%;
height: 200%;
transform: scale(.5);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.5);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.5);
}
}
@media (min--moz-device-pixel-ratio: 2.5), (-webkit-min-device-pixel-ratio: 2.5), (min-device-pixel-ratio: 2.5), (min-resolution: 240dpi), (min-resolution: 2.5dppx) {
.border::before {
width: 300%;
height: 300%;
transform: scale(.33333);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.33333);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.33333);
}
}
4. 解决移动端会出现300ms点击事件(click)延迟问题。通过引入fast-click库
5. 解决部分机型浏览器不支持promise的问题,需要在项目中安装babel-polyfill,它会自动判断当前机型是否支持promise,如果不支持会自动添加ES6的新特性。
rem是相对于根元素html的的font-size来说的,我们为根元素的font-size设置一个参考值,这个参考值决定了rem和px的换算量。即如果css里面没有设定html的font-size,由于浏览器html默认的font-size=16px,因此1rem=16px,即1px = 0.0625rem。通常,为了方便rem和px的转换,我们可以设置html:font-size=62.5%(即浏览器默认大小的62.5%:16*62.5%=10px),此时1rem=10px,1px=.1rem 方便了rem和px之间的换算。总之,1rem = html font-size 。
在引入的reset.css中,将html的font-size设置成了50px,所以rem是相对与50px的而言的,1rem = 50px。
有些css样式表的值都是相同的,比如页面的主题色、标题色、或字体大小等等,当更换主题时可以修改一次即可而不需要到每个页面一个一个的修改。这就是css全局变量的作用。
-
{{item.title}}
{{item.desc}}
在config/index.js中,可以定义接口访问的地址。
下图中代码的含义是:
target:在开发环境下,访问api路径时,会将该路径的请求发送到localhost:8080端口
pathRewrite:以api开头的请求会做一个url的跳转,如下即跳转到/static/mock目录下。(若不需要跳转,则可以直接省略pathRewrite属性不写)
Vuex应用于多个组件之间的数据传值,将共用数据存储于一个公共空间(state),当这个公共区域中的数据值被修改后,所有组件上应用的该变量都会同步被修改。
调用过程(如下图:)
store数值应用
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
city: 上海
}
})
......
import store from './store'
......
new Vue({
......
store,
......
})
store数值修改
方式一:components->actions->mutations->state->components
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
city: 上海
},
actions: {
changeCity (ctx, city) {
// ctx上下文对象中有commit方法
// commit方法将派发传递的参数为city到mutations的changeCity方法中
ctx.commit('changeCity', city)
}
},
mutations: {
changeCity (state, city) {
// state即state对象的city
state.city = city
}
}
})
方式二:components->mutations->state->components(适用于简单操作)
在一个网站应用中,我们通常需要进行数据的本地存储,在这里我们使用localstorage对象进行操作。另外,在大型的项目中,vuex通常是比较复杂的,将state、actions和mutations对象都放在index.js一个文件中是不合适的,代码会非常复杂。因此,就如同函数方法的拆分,我们可以把index.js文件中的state、actions、mutations拆分成三个js文件,以简化代码的复杂度。引入localstorage,并拆分后的源码:
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import actions from './actions'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
state,
actions,
mutations
})
src/store/state.js
let defaultCity = '上海'
// 若用户关闭了本地存储或使用隐身模式浏览网页,则localStorage会无法使用
// 因此使用trycatch处理
try {
if (localStorage.city) {
defaultCity = localStorage.city
}
} catch (e) { }
export default {
// 存储页面公用变量
city: defaultCity
}
src/store/actions.js(操作简单时可以省略actions环节)
export default {
// 通过Dispatch调用action,在通过commit进入mutations
changeCity (ctx, city) {
ctx.commit('changeCity', city)
}
}
src/store/mutations.js
export default {
// 修改state中的公用变量,并存储到localstorage中
changeCity (state, city) {
state.city = city
try {
localStorage.city = city
} catch (e) {}
}
}
优化组件调用store共用数据的方式:
...mapState()可以将state中的共用数据映射到本组件的数或对象中,因此在该组件中使用共用数据时,直接使用this.city调用即可。
{{this.city}}
优化组件修改store共用数据的函数调用方式:
mutations中有一个changeCity方法,使用...mapActions()将这个方法映射到本组件的changeCity方法中,因此在本组件中即可直接使用this.changeCity调用。
关于使用LocalStorage的扩展知识:
localStorage:HTML5新增的在浏览器端存储数据的方法。设置和获取localStorage的方法:
设置: localStorage.name = 'zjj';
获取: localStorage.name //zjj
sessionStorage: HTML5新增的在浏览器端存储数据的方法,设置和获取sessionStorage的方法:
设置: sessionStorage.name = 'zjj';
获取: sessionStorage.name //zjj
cookie:浏览器和服务器端都可以设置cookie,传统的用来存储数据的方法。
设置和获取方法见:http://www.cnblogs.com/zmj-blog/p/7119413.html
三者的关系和使用场景:
关系:
1.cookie在浏览器和服务器端来回传递数据,而localStorage和sessionStorage不会自动把数据发送给服务器,仅会保存在本地。cookie会在浏览器请求头或者ajax请求头中发送cookie内容。
2.cookie可以设置过期日期,sessionStorage是会话级的数据,浏览器窗口关闭即清楚,localStorage是永久性的数据,一旦赋值,不管多长时间这值都是存在的,除非手动清除。
3.cookie的存储大小受限制,一般不超过4k,而localStorage和sessionStorage的存储大小一般不超过5M,大大提高了存储的体积。
4.sessionStorage不跨窗口,在另外一个窗口打开sessionStorage就不存在了,它只在当前窗口有效,而cookie和localStorage都是跨窗口的,即使浏览器的窗口关闭,这两个值还是存在的。
使用场景:
localStorage可以用来统计页面访问次数。
sessionStorage可以用来统计当前页面元素的点击次数。
cookie一般存储用户名密码相关信息,一般使用escape转义编码后存储。
注:以上扩展内容摘自博文:localStorage,sessionStorage,cookie使用场景和区别,原作者:天空玉娇
export default new Router({
routes: [{
path: '/',
name: 'Home',
component: Home
}, {
path: '/city',
name: 'City',
component: City
}, { // 动态路由,带参数id,通过$route对象获取
path: '/detail/:id',
name: 'Detail',
component: Detail
}]
}
})
初始化后的vue应用是不能直接使用ip进行访问的。为了可以使用ip访问,需要修改配置。改好之后即可启动项目,使用ip进行访问了。在项目中的package.json添加红框中的配置项: