练习的是一个简单的前端页面,看着视频抄的,仿去哪儿的手机端,其中包含了git的使用、vue的基础点、一些插件的应用,在这里记录一下。
项目练习的源码地址: https://github.com/SeriousWatermelon/Vue-Demo.git 分支:vue-travel
目录
1.1 安装node.js运行环境: https://nodejs.org/en/download/
1. 项目环境准备
1.1 安装node.js运行环境: https://nodejs.org/en/download/
- 下载node.js完成后双击安装
- 命令行验证安装是否成功: node -v , npm -v
- 在码云或github中新建项目,进行项目托管。
- 下载并安装git
1.2 git的基础命令(gitbash中运行)
- git --version 查看git版本号
- git clone 项目https地址 克隆项目到本地
- git add . 添加修改过的文件和新建文件到暂存区
- git add -A 添加所有文件(删除、修改、新建)到暂存区
- git commit -m 'logs' 提交暂存区的文件到本地仓库,并添加logs日志
- git push 提交本地仓库的文件到当前所在的分支
- git branch 查看所有的分支
- git checkout branchName 切换到branchName分支
- git status 查看当前分支的状态(分支名)
- git merge branchName 合并branchName分支到当前所在的分支(这是本地的,需要push到远程仓)。
1.3 创建VUE项目(cmd中运行命令)
vue官网: https://cn.vuejs.org/v2/guide/installation.html
- 安装命令行工具vue-cli: npm install --global vue-cli
- 创建基于webpack模板的新项目: vue init webpack projectName 并进行项目的基础配置
a. 依次填写 项目名称(全英且不能有大写字母)
b. 项目描述
c. 作者
d. 采用的编译形式(RuntimeOnly只能线下编译和Runtime+Compiler(我暂时只会用这个)运行时可编译)
e. 是否安装vue-router(vue的路由,通常都需要)
f. 是否使用ESLint对代码格式校验(通常需要,保证代码的工整美)
g. 是否使用自动化测试(此处不需要)
h. 是否使用端到端测试(e2e)(此处不需要)
i. 使用什么工具作为包管理(使用npm) - 项目运行命令: npm run dev 或者 npm run start ,运行成功后可以在浏览器进行访问
- git代码托管
1.4 vue项目文件目录说明
|—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文件 项目说明文件
2. VUE项目开发零散知识点
2.1 多页面应用与单页面应用
多页面应用:每一次页面跳转,都会使用新的HTML;优点:首屏时间快(页面收个内容展示出的时间;用户请求->服务器返回url,只经历了一个http请求);SEO(搜索引擎)效果优化好。缺点:页面切换较慢。
单页面应用:每一次跳转都是js动态删除当前的页面,再渲染下一个页面。优点:页面切换快。缺点:首屏时间稍慢,SEO差。
2.2 <router-link>标签
跳转标签,类似于html5的a标签
<router-link tag="div" to="/">
<div><img class="item-img" src="imgUrl" /></div>
</router-link>
补充:编程式导航
在js事件处理完毕后,若需要跳转到其他页面,vue中我们可以使用路由跳转 this.$router.push('/home')。类似于传统的location.href。
2.3 移动端网页注意项
1. 禁止双指放大或缩小网页,需要在最外层的index.html中,添加如下设置
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
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库
- 安装fast-click 第三方模块 npm install fastclick --save
- 在main.js中引入fast-click import fastClick from 'fastClick'
- 在main.js中将fast-click组件绑定到document.body fastClick.attach(document.body)
5. 解决部分机型浏览器不支持promise的问题,需要在项目中安装babel-polyfill,它会自动判断当前机型是否支持promise,如果不支持会自动添加ES6的新特性。
- 安装babel-polyfill: npm install babel-polyfill --save
- 在src/main.js中引入babel-polyfill,import 'babel-polyfill'
2.4 iconfont的使用
- 登录iconfont官网(阿里巴巴矢量图) http://www.iconfont.cn/
- 图标管理—> 我的项目,新建一个项目
- 浏览图标库,将选中的icon放入购物车中
- 点击购物车,添加至项目到刚才新建的项目中
- 返回我的项目,将项目中所有的图标 下载至本地
- 将iconfont.eot、iconfont.svg、iconfont.ttf、iconfont.woff四个文件引入到(/src/assets/styles/iconfont)目录下
- 将iconfont.css引入到( /src/assets/styles )目录下,并修改iconfont.css中引入上面四个文件的路径名
- main.js中引入iconfont.css: import './assets/styles/iconfont.css'
- 页面使用: <div class="iconfont"></div> 根据图标的代码进行调用。
2.5 stylus格式的css使用
- 在cmd端对项目安装stylus: npm install stylus --save
- 在cmd端对项目安装stylus-loader: npm install stylus-loader --save
- css格式参照下图。scoped关键字可以限制css样式局部化,只对当前页面有效。
2.6 px和rem单位转换
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。
2.7 Vue配置CSS的全局变量
有些css样式表的值都是相同的,比如页面的主题色、标题色、或字体大小等等,当更换主题时可以修改一次即可而不需要到每个页面一个一个的修改。这就是css全局变量的作用。
- /src/assets/styles目录下,新建varibles.styl格式的文件,按照下图格式定义常量内容
- 使用时,在style标签中先引入该文件
- 使用方法: background: $bgColor
2.8 vue中的文件路径
- 在vue中,@符号表示src目录。在css表中(如2.7引入varibles.styl)引入文件时,会变得更加简洁:
@import '~@/assets/styles/varibles.styl ' - 配置styles目录
1. 需要配置的文件:/build/webpack.base.conf.js
2. 找到resolve对象 ,可以看到 resolve对象中的alias对象,用于配置文件目录的别名
3. 在resolve对象的alias对象中,新添一个目录别名: 'styles': resolve('src/assets/styles') 即可。(也可以看到,@代表src目录的原因)(修改后需要重新启动项目)
4. 此时,2.7中引入varibles.styl的代码又可以优化成:
@import ' ~styles/varibles.styl '
并且main.js中我们之前引入的文件也可以进行优化了:
>>>>>>>>>>>>>>
2.9 vue中使用ajax请求数据
- 安装axios第三方模块,他可以实现跨平台的数据请求: npm install axios --save 重启服务器。
- 需要进行ajax请求的单页面引入axios: import axios from 'axios'
- 使用ajax钩子函数和axios联合进行数据获取:
<template> <div> <home-weekend :list="weekendList"></home-weekend> </div> </template> <script> import HomeWeekend from './components/Weekend' import axios from 'axios' export default{ name: 'Home', components: { // 声明组件 HomeWeekend: HomeWeekend }, data () { return { weekendList: [] } }, methods: { getHomeInfo () { axios.get('/api/index.json?city=' + this.city) .then(this.getHomeInfoSucc) }, getHomeInfoSucc (res) { res = res.data if (res.ret && res.data) { const data = res.data this.weekendList = data.weekendList } } }, mounted () { this.getHomeInfo() } } </script>
- 子组件接收父组件传递的值并进行数据渲染
<template> <div> <ul> <li class="item border-bottom" v-for="item of list" :key="item.id"> <div class="item-img-wrapper"> <img class="item-img" :src="item.imgUrl" /> </div> <div class="item-info"> <p class="item-title">{{item.title}}</p> <p class="item-desc">{{item.desc}}</p> </div> </li> </ul> </div> </template> <script> export default{ name: 'HomeWeekend', props: { list: Array } } </script>
在config/index.js中,可以定义接口访问的地址。
下图中代码的含义是:
target:在开发环境下,访问api路径时,会将该路径的请求发送到localhost:8080端口
pathRewrite:以api开头的请求会做一个url的跳转,如下即跳转到/static/mock目录下。(若不需要跳转,则可以直接省略pathRewrite属性不写)
2.10 Vuex数据层框架
Vuex应用于多个组件之间的数据传值,将共用数据存储于一个公共空间(state),当这个公共区域中的数据值被修改后,所有组件上应用的该变量都会同步被修改。
调用过程(如下图:)
- 所有的共用数据存储在state仓库。
- components组件直接调用state仓库中的数据。
- 当需要改变state时,组件不能直接修改:
1. 若有异步操作或复杂的同步操作需要修改数据,将操作过程放在actions中。组件通过dispatch函数调用actions中的操作过程;
2. actions通过commit调用Mutations,mutations中是对state仓库共用数据的修改。只有mutations能修改state的数据(但是,当过程不复杂是,也可以组件直接调用mutations,来操作state数据,可以不经过actions)。
store数值应用
- 项目中安装vuex: npm install vuex --save
- 由于大型项目中,vuex是比较复杂的,因此可以在src目录下新建一个store目录,存放vuex的相关配置
- 在src/store/index.js文件中引入vue和vuex,并创建Store对象。
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { city: 上海 } })
- 在src/main.js中引入该文件:
...... import store from './store' ...... new Vue({ ...... store, ...... })
- 到现在为止,如果要使用city这个store的变量,只需组件中使用即可获取到值: {{ this.$store.state.city }} (可优化,参见2.12)
store数值修改
方式一:components->actions->mutations->state->components
- 组件中使用js调用actions: this.$store.dispatch('changeCity', city) ,派发传递参数为city的变量到actions的changeCity函数中。
- 在src/store/index.js文件中,需要定义actions的changeCity方法,changeCity方法需要commit数据到mutations的方法中,mutations中的函数才能进行state数据的修改。下面代码表示,actions的changeCity()函数接收到dispatch派发过来的参数后,通过上下文对象ctx,调用commit()函数,将city派发到mutations的changeCity()方法中
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(适用于简单操作)
- 组件中js直接调用commit传值给mutations:this.$store.commit('changeCity', city) 组件派发给mutations的changeCity函数,并传递参数city。
- 此时,src/store/index.js中,已经不需要(可以删掉)actions对象。
2.11 Vuex的高级使用localstorage
在一个网站应用中,我们通常需要进行数据的本地存储,在这里我们使用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调用即可。
- 在应用store对象数据的组件页面的js中,引入mapState: import { mapState } from 'vuex'
- 在computed计算属性中,使用展开运算符传递参数(数组或对象)
<template> <div class="header"> <router-link to='/city'> {{this.city}} </router-link> </div> </template> <script> import {mapState} from 'vuex' export default{ name: 'HomeHeader', computed: { // mapState映射的是一个数组,数组只有一个city元素 // 将vuex中的数据映射到computed计算属性的city中 ...mapState(['city']) } } </script>
<template> <div class="list" ref="wrapper"> <div class="button">{{this.currenCity}}</div> </div> </template> <script> import mapState from 'vuex' export default{ // 省略...... computed: { ...mapState({ // 对象 // 将vuex中的共用数据city,映射到组件计算属性的currentCity中 currenCity: 'city' }) } //省略...... </script>
优化组件修改store共用数据的函数调用方式:
mutations中有一个changeCity方法,使用...mapActions()将这个方法映射到本组件的changeCity方法中,因此在本组件中即可直接使用this.changeCity调用。
<template>
<!-- 省略 -->
</template>
<script>
// ……省略
import {mapState, mapActions} from 'vuex'
export default{
// ……省略
methods: {
handleCityClick (city) {
// 通过dispatch调用vuex的actions
// this.$store.dispatch('changeCity', city)
this.changeCity(city)
// 页面路由跳转
this.$router.push('/')
},
...mapActions(['changeCity'])
},
// ……省略
}
</script>
<style lang="stylus" scoped>
/* 省略 */
</style>
关于使用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使用场景和区别,原作者:天空玉娇
2.12 带参数的路由跳转
- 配置带参数的路由(router/index.js)
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 }] } })
- router-link跳转
<router-link tag="li" class="item border-bottom" v-for="item of list" :key="item.id" :to="'/detail/'+item.id"> <img class="item-img" :src="item.imgUrl" /> </router-link>
- 目标页面进行参数接收: this.$route.params.id
2.13 通过ip访问vue项目
初始化后的vue应用是不能直接使用ip进行访问的。为了可以使用ip访问,需要修改配置。改好之后即可启动项目,使用ip进行访问了。在项目中的package.json添加红框中的配置项:
2.14 tomcat的简单部署
- 在/config/index.js中配置vue应用访问的目录名:修改build的assetsPublicPath值,使其值等于将要放到tomcat的webapps中的目录名。
- 项目打包: npm run build ,当cmd出现build complete后即打包完成。项目目录中会多出一个dist目录,即打包完成的项目。
- 将dist文件放到tomcat的webapps目录中,启动tomcat,即可进行访问vue应用(localhost:8080/dist)。注意更换json请求url。