创建Vue项目
# 全局安装 vue-cli
$ cnpm install --global vue-cli
# 创建一个基于 webpack 模板的新项目
$ vue init webpack my-project
项目运行
# 开发者模式运行[默认访问:localhost:8080]
$ npm run dev
# 打包运行[默认访问:localhost:5000]
$ npm run build
$ npm install -g serve
$ serve dist
开发目录设计
src
》api 与后台交互模块文件夹
》common 通用资源文件夹,如fonts/img/stylus
》components 非路由组件文件夹
》filter 自定义过滤器模块文件夹
》mock 模拟数据接口文件夹
》pages 路由组件文件夹
》router 路由器文件夹
》store vuex相关模块文件夹
- App.vue 入口应用组件
- main.js 入口JS
依赖stylus
$ npm install stylus stylus-loader --save-dev
# App.vue里的<style>改成如下:
<style lang='stylus' rel='stylesheet/stylus'>
使用stylus
- 结构:通过缩进控制,不需要大括号和分号,冒号是可选的
- 父级引用:使用字符&指向父选择器
- 定义变量(推荐变量以$开头): name=value
- 引用变量:name
- 导入:通过@import引入其他样式文件
引入Reset CSS
重置浏览器标签的样式表:Reset CSS链接
src同目录下的static文件夹下创建css/reset.css文件
访问链接并复制里面的CSS样式粘贴到reset.css文件中
index.html引入
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>yu-mall</title>
<link rel="stylesheet" href="http://at.alicdn.com/t/font_867696_7k42ws2p9ew.css">
<link rel="stylesheet" href="/static/css/reset.css">
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
引用图标库
访问阿里巴巴图标库选择图标创建项目后获取样式链接
在index.html文件对应的引入
<link rel="dns-prefetch" href="http://at.alicdn.com/t/font_867696_7k42ws2p9ew.css"/>
创建底部导航点击后访问的路由组件
引入与配置vue-router
# 安装
$ npm install vue-router --save
# 创建路由配置JS 路径:src/router/index.js
# index.js代码块
import Vue from 'vue'
import Router from 'vue-router'
import Home from '../pages/Home/Home'
import Category from '../pages/Category/Category'
import Cart from '../pages/Cart/Cart'
import User from '../pages/User/User'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
redirect: '/Home' // 重定向到首页
},
{
path: '/Home',
component: Home // 首页
},
{
path: '/Category',
component: Category // 分类
},
{
path: '/Cart',
component: Cart // 购物车
},
{
path: '/User',
component: User // 我的
}
]
})
# main.js代码块
import Vue from 'vue'
import App from './App'
import router from './router/index'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
创建底部导航非路由组件
路径:src/components/FooterGuide/FooterGuide.vue
<template>
<div class="footer-guide">
<div class="guide-item" v-for="guide in guides" :class="{on:guide.path === $route.path}" @click="goTo(guide.path)">
<span class="item-icon">
<i class="iconfont" :class="[guide.path === $route.path ? guide.choice_icon : guide.icon]"></i>
</span>
<span>{{ guide.text}}</span>
</div>
</div>
</template>
<script>
export default {
name: 'FooterGuide',
data () {
return {
'guides': [
{
text: '首页',
icon: 'icon-home',
choice_icon: 'icon-homefill',
path: '/home'
},
{
text: '分类',
icon: 'icon-form_light',
choice_icon: 'icon-formfill',
path: '/category'
},
{
text: '购物车',
icon: 'icon-cart',
choice_icon: 'icon-cartfill',
path: '/cart'
},
{
text: '我的',
icon: 'icon-people',
choice_icon: 'icon-peoplefill',
path: '/user'
}
]
}
},
methods: {
goTo (path) {
this.$router.replace(path)
}
}
}
</script>
<style lang='stylus' rel='stylesheet/stylus'>
@import "../../common/stylus/mixins.styl" /* $hr=1px #EEE solid */
.footer-guide
border-top $hr
position fixed
z-index 100
left 0
bottom 0
background #FFF
width 100%
height 50px
display flex
.guide-item
text-decoration none
display flex
flex 1
text-align center
flex-direction column
align-items center
margin 5px
color #8a8a8a
&.on
color #ff001e
span
font-size 12px
margin-top 2px
margin-bottom 2px
.iconfont
font-size 22px
</style>
将该组件添加到App.vue中
<template>
<div id="app">
<router-view/>
<footer-guide/>
</div>
</template>
<script>
import FooterGuide from './components/FooterGuide/FooterGuide'
export default {
name: 'App',
components: {
FooterGuide
}
}
</script>
效果图:
创建通用顶部搜索框非路由组件
路径:src/components/HeaderSearch/HeaderSearch.vue
<template>
<div class="header-search">
<div class="search-box">
<a href="/search"><i class="iconfont icon-search"></i><span>请输入你想找的商品</span></a>
</div>
</div>
</template>
<script>
export default {
name: 'HeaderSearch',
data () {
return {
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import "../../common/stylus/mixins.styl"
.header-search
width 100%
height 44px
background #FFF
border-bottom $hr
.search-box
background: #f3f5f4;
border-radius: 14px;
height: 28px;
overflow: hidden;
position relative;
top: 8px
margin 0 10px
a
text-decoration none
i
position absolute
top 5px
left 10px
color #999
font-size 18px
font-weight bold
span
font-size 14px
color #ccc
position absolute
left 36px
line-height 28px
</style>
将该组件引入Home.vue和Category.vue中
/* Home.vue */
<template>
<div class="home">
<header-search/>
</div>
</template>
<script>
import HeaderSearch from '../../components/HeaderSearch/HeaderSearch'
export default {
name: 'home',
components: {
HeaderSearch
}
}
</script>
/* Category.vue */
<template>
<div class="category">
<header-search/>
</div>
</template>
<script>
import HeaderSearch from '../../components/HeaderSearch/HeaderSearch'
export default {
name: 'category',
components: {
HeaderSearch
}
}
</script>
运行效果
创建广告轮播组件
路径:src/components/Banner/Banner.vue
<template>
<div class="banner" ref="bannerImg" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
<ul class="banner-img"
@transitionend="transitionend"
:style="{width: 100*(bannerData.length+2)+'%',transform: 'translateX('+translateX+'px)'}"
:class="{transform:!isMove}">
<li>
<a :href="bannerData[bannerData.length-1].path">
<img :src="bannerData[bannerData.length-1].imgUrl"/>
</a>
</li>
<li v-for="data in bannerData">
<a :href="data.path">
<img :src="data.imgUrl"/>
</a>
</li>
<li>
<a :href="bannerData[0].path">
<img :src="bannerData[0].imgUrl"/>
</a>
</li>
</ul>
<ul class="banner-bullet" :style="{'margin-left': -(16*this.bannerData.length)/2 + 'px'}">
<li v-for="(data,i) in bannerData" :class="{on:i === bulletIndex }"></li>
</ul>
</div>
</template>
<script>
export default {
name: 'home',
props: ['banner-data'], /* {imgUrl:'',path:''} */
data () {
return {
width: 0,
index: 1, /* 0~bannerData.length + 1 */
translateX: '0px',
isMove: true,
bannerTouch: {
startX: 0,
endX: 0,
distanceX: 0
},
timer: {},
timerData: 3000 /* 不能小于或等于过渡时间200秒 */
}
},
mounted () {
let p = this
this.width = this.$refs.bannerImg.clientWidth
this.translateX = -this.width
this.timer = setInterval(function () {
p.isMove = false
p.index++
p.translateX = -p.index * p.width
}, this.timerData)
},
computed: {
bulletIndex () {
if (this.index >= this.bannerData.length + 1) {
return 0
} else if (this.index <= 0) {
return this.bannerData.length - 1
}
return this.index - 1
}
},
methods: {
touchstart (ev) {
this.bannerTouch.startX = ev.touches[0].clientX
},
touchmove (ev) {
clearInterval(this.timer)
var move = ev.touches[0].clientX
this.bannerTouch.distanceX = move - this.bannerTouch.startX
this.translateX = -this.index * this.width + this.bannerTouch.distanceX
this.isMove = true
},
touchend (ev) {
if (Math.abs(this.bannerTouch.distanceX) < this.width / 3) {
} else if (this.bannerTouch.distanceX > 0) {
this.index-- // = this.bannerData.length // 切换到倒数2
} else { // if (this.index >= this.bannerData.length + 1) {
this.index++
}
this.translateX = -this.index * this.width
this.isMove = false
let p = this
clearInterval(this.timer)
this.timer = setInterval(function () {
p.isMove = false
p.index++
p.translateX = -p.index * p.width
}, this.timerData)
},
transitionend () {
if (this.index >= this.bannerData.length + 1) {
this.index = 1
this.isMove = true
this.translateX = -this.index * this.width
} else if (this.index <= 0) {
this.index = this.bannerData.length
this.isMove = true
this.translateX = -this.index * this.width
}
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
.banner
width 100%
height auto
overflow-x hidden
position relative
.banner-img
display flex
&.transform
transition all .2s
li
flex 1
a
display block
img
width 100%
display block
border none
.banner-bullet
position absolute
bottom 15px;
z-index 101
display flex
left 50%
li
&.on:before
background #bc0000
box-shadow 0px 0px 3px #bc0000
li:before
content ''
width 6px
height 6px
border-radius 3px
border 1px solid #FFF
display block
margin 0px 4px
box-shadow 0px 0px 3px #ccc
</style>
修改Home.vue,引入该组件
<template>
<div class="home">
<header-search/>
<banner :banner-data="bannerData"/>
</div>
</template>
<script>
import HeaderSearch from '../../components/HeaderSearch/HeaderSearch'
import Banner from '../../components/Banner/Banner'
export default {
name: 'home',
components: {
Banner,
HeaderSearch
},
data () {
return {
bannerData: [
{
imgUrl: 'http://upload.shopncdemo.com/image/d5/3d/d53d507063c5195c74f24aabd9a0ec8a.jpg',
path: '/100'
},
{
imgUrl: 'http://upload.shopncdemo.com/image/06/48/064879ea8043a03787ec849c10fa4400.jpg',
path: '/2'
},
{
imgUrl: 'http://upload.shopncdemo.com/image/fb/c6/fbc658c0075ef4743e8cf0dd484a3796.jpg',
path: '/3'
},
{
imgUrl: 'http://upload.shopncdemo.com/image/24/65/24656dd4e23f398abb9afc2543b8f8de.jpg',
path: '/4'
}
]
}
}
}
</script>
运行效果:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。