文章目录
1. 运行以下命令全局安装脚手架
新版本(推荐)
npm i @vue/cli -g
老版本
npm i vue-cli -g
你还可以用这个命令来检查其版本是否正确:
vue --version
2. 运行以下命令来创建一个新项目
vue create hello-world
3. 项目目录
src
- api - 封装数据请求
- assets - 资源文件夹
- components - 自定义组件
- filters - 自定义过滤器
- lib - scss库
- mixins - 混入
- router - 路由 ---- 动态加载路由实现权限管理
- store - 状态管理器 --- 管理状态 - 善于利用辅助函数
- views - 页面
- App.vue - 主页面 - 单文件组件(结构 + 表现 + 行为)
- main.js 入口文件 - 可以引入国际化(i18n)、全局操作
vue.config.js --- vue项目配置文件 --- 根目录
3. 命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 content(内容) 和 Footer(底部) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。
App.vue
<template>
<div id="app">
<router-view ></router-view>
<router-view name="Footer"></router-view>
</div>
</template>
<script>
</script>
<style>
....
</style>
./views/home/index(页面组件首页为例)
<template>
<div class="conent">
<h1>首页</h1>
</div>
</template>
<script>
</script>
<style>
....
</style>
./compontents/Footer.vue(底部组件)
点击底部tabBar进行声明式导航
<template>
<div class="Footer">
<div>
<ul class="box">
<router-link to="/home" tag="li">首页</router-link>
<router-link to="/kind" tag="li">分类</router-link >
<router-link to="/cart" tag="li">购物车</router-link>
<router-link to="/user" tag="li">我的</router-link >
</ul>
</div>
</div>
</template>
<script>
</script>
<style>
....
</style>
./router/index.js(路由配置文件)
import Vue from 'vue'
import VueRouter from 'vue-router'
import Footer from '@/components/Footer/index'
Vue.use(VueRouter)
const routes = [
{ // 重定向
path: '/',
redirect: '/home'
},
{
path: '/home',
name: 'home', 命名路由,可以用于声明式导航传参
components: {
default: () => import('@/views/home/index.vue'),// 路由的懒加载
Footer: Footer
}
},
{
path: '/cart',
name: 'cart',
components: {
default: () => import('@/views/cart/index.vue')
//购物车页面这里不需要底部所以我们这没有Footer组件 !
}
},
{
path: '/user',
name: 'user',
components: {
default: () => import('@/views/user/index.vue'),
Footer: Footer
}
},
{
path: '/kind',
name: 'kind',
components: {
default: () => import('../views/kind/index.vue'),
Footer: Footer
}
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
页面效果
注意看购物车页面是没有底部组件的
vue封装axios请求
import axios from 'axios'
// 1.通过当前的运行命令判断出当前的运行环境,切换请求地址
// 开发环境 + 测试环境 + 生产环境
const isDev = process.env.NODE_ENV === 'development'
// isDev --- true ---- 开发环境 ---- npm run serve
// isDev ---- false ---- 生产环境 ---- npm run build
// 如果是开发环境,服务器可能是本地的,也可能是服务器上的
const baseUrl = isDev ? 'http://www.baidu.net/api' : 'http://www.baidu.com/api'
// 2.自定义axios,添加axios拦截器
const instance = axios.create({
baseURL: baseUrl
})
// 在实例已创建后修改默认值,比如请求时需要拿到token值
// instance.defaults.headers.common['token'] = token
// 自定义超时时间
instance.defaults.timeout = 6000
// 每一次发送请求前都需要经过这里 - 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么 -- 显示loading动画,携带token信息
const token = localStorage.getItem('token')
config.headers.common.token = token
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
// 每一次拿到数据都需要经过这里 - 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么 --- 关闭loading动画
return response
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error)
})
export default instance
import request from './index'
/**
* 封装首页轮播图请求
*/
export function getBannerlist (params) {
const { url } = params
return request({
url,
method: 'GET'
})
}
/**
* 封装首页列表请求
*/
export function getProlist (params) {
const { url, data } = params
// return request({
// url,
// data:
// method: 'GET'
// })
return request.get(url, {
params: data || {}
})
}
嵌套路由
./router/index.js(路由配置文件)
{
path: '/user',
name: 'user',
components: {
default: () => import('@/views/user/index.vue'),
Footer: Footer
},
children: [
{
path: 'nologin',
component: nologin
},
{
path: 'loging',
component: loging
}
]
},
./views/index.vue
<template>
<div class="content">
<h1>个人中心</h1>
<router-view ></router-view>
</div>
</template>
<script>
</script>
路由守卫
./store/index(vuex全局仓库)
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token: localStorage.getItem('token'),
username: localStorage.getItem('username')
},
mutations: {
set_token: function (state, token) {
state.token = token
},
set_username: function (state, username) {
state.username = username
}
},
actions: {
},
modules: {
}
})
./views/nologin(登录页面组件)
//储存token,username信息到本地存储和vuex
methods: {
onSubmit (values) {
console.log('submit', values)
request({
method: 'post',
url: '/users/login',
data: {
tel: values.tel,
password: values.password
}
}).then(res => {
if (res.data.code === '10008') {
alert('成功')
this.$store.commit('set_token', res.data.data.token)
this.$store.commit('set_username', res.data.data.username)
localStorage.setItem('token', res.data.data.token)
localStorage.setItem('username', res.data.data.username)
this.$router.push('/user/loging')
} else if (res.data.code === '10007') {
alert('未注册')
} else {
alert('密码错误')
}
})
}
}
./router/index
router.beforeEach((to, from, next) => {
// 一定要调用next()方法来resolve这个钩子
//全局守卫判断有无token信息,路由跳转逻辑
if (to.path === '/user') {
console.log(store.state.token)
if ((store.state.token)) {
next('/user/loging')
} else {
next('/user/nologin')
}
} else if (to.path === '/user/loging') {
if (store.state.token) {
next()
} else {
next('/user/nologin')
}
} else if (to.path === '/user/nologin') {
if (store.state.token) {
next('/user/loging')
} else {
next()
}
} else {
next()
}
})
首页组件
./views/home/index
首页轮播图渲染
// 使用vant组件库
<template>
<div class="content">
<van-swipe :height="200" :width="300" :loop="false" class="my-swipe" :autoplay="3000" indicator-color="white"> // 轮播图
<van-swipe-item v-for="(banner, index) in banners" :key="index"><img :src="www.baidu.com" alt="">{{banner}}</van-swipe-item>
</van-swipe>
<Prolist/>
<vueToTop bottom='100' type='4' color='red'></vueToTop> // 回到顶部的插件
</div>
</template>
<script>
import Vue from 'vue'
import { Swipe, SwipeItem } from 'vant'
import Prolist from '../../components/Prolist'
import request from '../../api/index'
import vueToTop from 'vue-totop'
Vue.use(Swipe)// 轮播
Vue.use(SwipeItem)
Vue.use(vueToTop)
export default {
components: {
Prolist
},
data () {
return {
banners: []
}
},
created () {
this.getBanner() // 请求轮播图
},
methods: {
getBanner: function () {
request({
method: 'get',
url: '/pro/banner'
}).then(res => {
this.banners = res.data.data
})
}
}
}
</script>
<style>
.my-swipe .van-swipe-item {
color: #fff;
font-size: 20px;
line-height: 150px;
text-align: center;
background-color: #39a9ed;
}
img {
height: 100%;
width: 100%;
}
</style>
首页Prolist组件
<template>
<div class="list">
<van-pull-refresh v-model="isLoading" @refresh="onRefresh">
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
<div class="list_content" v-for="pro in prolists" :key="pro.proid" @click="todetail(pro.proid)">
<div class="list_img">
<img :src="pro.proimg" alt="">
</div>
<div class="list_del center">
<ul class="list_items">
<li>{{pro.proname}}</li>
<li>库存:{{pro.sales}}</li>
<li style="color: red"><span style="color: black">¥:</span>{{pro.price}}</li>
</ul>
</div>
</div>
</van-list>
</van-pull-refresh>
</div>
</template>
<script>
import Vue from 'vue'
import { PullRefresh, Toast, List } from 'vant'
import request from '../../api/index'
Vue.use(PullRefresh)
Vue.use(List)
export default {
data () {
return {
prolists: '',
pageCode: 1,
isLoading: false,
loading: false,
finished: false
}
},
created: function () {
request({
method: 'get',
url: '/pro'
}).then(res => {
this.prolists = res.data.data
})
},
methods: {
onRefresh () { // 下拉刷新
request.get('pro', {
params: {
pageCode: 1
}
}).then(res => {
this.isLoading = false
this.prolists = res.data.data
Toast('刷新成功')
console.log(this.prolists)
})
},
todetail (proid) {
this.$router.push('/detail/' + proid) // 路由传参跳转
},
onLoad () { // 上拉加载
this.loading = true
request.get('pro', {
params: {
pageCode: this.pageCode
}
}).then(res => {
this.loading = false
this.pageCode++
if (res.data.code === '10000') {
this.finished = true
} else {
this.prolists = [...this.prolists, ...res.data.data]
}
})
}
}
}
</script>
<style scoped>
.list {
padding-bottom: 40px;
}
.center {
display: flex;
align-items: center;
}
.list_content {
display: flex;
padding: 1rem;
}
.list_img {
padding: 0rem 1.5rem 0rem 0rem;
}
.list_del {
width: 100%;
}
</style>>
动态路由传参
跳转页面
<div class="list_content" v-for="pro in prolists" :key="pro.proid" @click="todetail(pro.proid)">
<div class="list_img">
<img :src="pro.proimg" alt="">
</div>
<div class="list_del center">
<ul class="list_items">
<li>{{pro.proname}}</li>
<li>库存:{{pro.sales}}</li>
<li style="color: red"><span style="color: black">¥:</span>{{pro.price}}</li>
</ul>
</div>
</div>
//js部分
todetail (proid) {
this.$router.push('/detail/' + proid)//点击事件传递proid参数
}
路由文件
{
path: '/detail/:proid',//动态绑定路由 注意!!!这里必须绑定(:proid)不然跳转到详情页刷新后就获取不到路由信息了
name: 'detail',
component: () => import('../views/detail/index.vue')
}
被跳转页面(detail页面)
<script>
import request from '../../api/index'
export default {
data () {
return {
proDetail: []
}
},
mounted: function () {
const { $route: { params: { proid } } } = this//this.$router.params获取路由参数proid
request({//使用proid字段请求数据
method: 'GET',
url: '/pro/detail',
params: {
proid
}
}).then(res => {
this.proDetail = res.data.data
})
}
}
</script>