Vue实战
创建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>
运行效果:
相关推荐
yuzhu 2020-11-16
85477104 2020-11-17
KANSYOUKYOU 2020-11-16
sjcheck 2020-11-03
怪我瞎 2020-10-28
源码zanqunet 2020-10-28
gloria0 2020-10-26
王军强 2020-10-21
学习web前端 2020-09-28
QiaoranC 2020-09-25
anchongnanzi 2020-09-21
安卓猴 2020-09-12
Macuroon 2020-09-11
kiven 2020-09-11
LittleCoder 2020-09-11
Cheetahcubs 2020-09-13
小焊猪web前端 2020-09-10
颤抖吧腿子 2020-09-04
softwear 2020-08-21