首先,慕课网提供的视频是基于vue1.0的规范写的,所以有一些地方在vue2.0下不能正常运行。虽然课程补充文件也提供了vue2.0的代码,但多多少少也有一些问题。参考他人写的代码以及官方文档,修改了部份内容,整个项目可以流畅运行,不会报错。javascript
项目请移步个人github地址:https://github.com/gqbwoai2016/-VUE2.0-APP-。里面内容还不是不少,之后会继续丰富的。css
-------------------------------------------------------------------------------------------------------------------------------------------------html
废话就这么多,开始实质的内容吧:前端
涉及的工做有:由商家模块入手,进行需求分析,利用脚手架工具搭建,mock数据,架构设计,代码实现,自测以及编译打包。本项目注重代码规范:架构设计,组建抽象,模块拆分,代码风格统一,JS变量命名规范及CSS代码规范。vue
所涉及的技术有:vue-cli脚手架工具;vue-resource与后端数据交互(相似AJAX);vue-router插件管理路由;第三方库better-csroll;最大程度组件化(优势:可维护性和复用性);localstorage;flex弹性布局;java
实现的功能node
• Goods、Ratings、Seller组件视图都可上下滚动 • 商品页 点击左侧menu,右侧list对应跳转到相应位置 • 点击list查看商品详情页,父子组件的通讯 • 评论内容够能够筛选查看 • 购物车组件,包括添加删除商品及动效,购物控件与购物车组件之间非父子组件通讯,点击购物车图标,展现选择的商品列表 • 商家实景图片能够左右滑动 • loaclStorage缓存商家信息(id、name)
0、用vue-cli脚手架工具搭建项目结构,有如下结构webpack
项目名称 ->build ------------------------------------------ webpack配置相关文件 ->config ------------------------------------------ webpack配置相关文件 ->node_modules ------------------------------------------ 依赖代码库 ->src ------------------------------------------ 存放源码 ->common ---- 通用的css和fonts ->components ---- Vue 组件 ->router ---- vue-router相关配置(linkActiveClass,routes注册组件路由) ->build ---- webpack 的打包编译配置文件 ->config ---- 一些配置项,好比咱们服务器访问的端口配置等 ->dist ---- 项目通过 build 以后才会生成 ->prod.server.js----模拟的服务器配置,用来运行dist里面的文件,在config/index.js中,build对象中 添加一条端口设置port:9000, ->App.vue ---- 根组件,全部的子组件都将在这里被引用 ->index.html ---- 整个项目的入口文件,将会引用咱们的根组件 App.vue ->main.js ---- 入口文件的 js 逻辑,在 webpack 打包以后将被注入到 index.html 中 ->static ------------------------------------------ 存放第三方静态资源 ->.babelrc.JSON ------------------------------------------ babe的配制,用于从es6编译至es5 ->.editorconfig ------------------------------------------ 编译器配置 ->.eslintignore ------------------------------------------ 忽略语法检查的目录文件 ->.eslintrc.js ------------------------------------------ eslint的配置 ->.gitignore ------------------------------------------ git忽略的目录或文件 ->index.html ------------------------------------------ 入口文件,项目编译过程当中会自动插入其中 ->package.json ------------------------------------------ 配置文件,描述一个项目 ->README.md ------------------------------------------ 项目描述文件
全部项目文件放在src下,main.js为入口文件,APP.vue为整个页面的vue,components存放组建文件,里面有许多子目录,子目录中存放.vue文件及图片等资源(知足组件化开发的就近维护)。common包含公共模块资源(js,scss,fonts等)。 ios
一、 数据mockcss3
mock就是作假数据,这样能够便于先后端分离开发,前端不须要等后端作好数据来开发或者测试验证
假数据data.json.实现:sell(项目总文件)->插入data.json,而后在build->webpack.dev.conf.js中写数据接口,而后写数据的路由。
坑1,最新版的vue中dev-server.js被替换成了webpack.dev.conf.js,修改时注意
二、reset
sell->static->css->reset.css,写好后给index.html用link标签引用这个文件
三、meta
对其设置viewport以规范显示大小,缩放等移动端视口初始化。
四、header组件开发
a)使用vue-router实现点击切换;在这里,当前模块样式在vue-router中有个默认属性linkActiveClass,在main.js对其配置后,在APP.vue中经过<vue-link to:"/goods">建立点击部分以及<router-view>建立展现部分;
vue-router详解:
路由,其实就是指向的意思。咱们页面中全部内容都是组件化的,咱们只要把路径和组件对应起来就能够了,而后在页面中把组件渲染出来。
页面实现:定义了两个标签<router-link> 和<router-view>来对应点击和显示部分。<router-link> 就是定义页面中点击的部分,<router-view> 定义显示部分,就是点击后,区配的内容显示在什么地方。因此 <router-link> 还有一个很是重要的属性 to,定义点击以后,要到哪里去, 如:<router-link to="/home">Home</router-link>
js中配置路由:首先定义route,由path,component组成,前者指路径,后者指组件,以下所示,最后建立router管理路由。配置完成后,把router 实例注入到 vue 根实例中,就可使用路由了
const routes = [ { path: '/home', component: Home }, { path: '/about', component: About } ] //管理路由 const router = new VueRouter({ routes // routes: routes 的简写 }) //配置完成,注入到实例中 const app = new Vue({ router }).$mount('#app')//$mount为手动挂载 //上面这个写法也能够改成 new Vue({ el:"#box", router });
注意:当首次进入页面的时候,页面中并无显示任何内容。这是由于首次进入页面时,它的路径是 '/',咱们并无给这个路径作相应的配置。在这里使用重定向解决问题。
const routes = [ { path:"/home", component: home }, { path: "/about", component: about }, // 重定向 { path: '/', redirect: '/home' } ]
b)1像素实现:在APP端因为dpr不一样,展现会有问题,所以添加minxin.scss设置通用样式后再经过base.scss使用@media 媒体查询来分别设置不一样dpr的scale控制样式;
window.devicePixelRatio是设备上物理像素和设备独立像素(device-independent pixels (dips))的比例。公式表示就是:window.devicePixelRatio = 物理像素 / dips
非视网膜屏幕的iphone上等于1,在视网膜屏幕的iphone上,屏幕物理像素640像素,独立像素仍是320像素,所以等于2。
2x和3x图是为了适应不一样dpr比例的,不一样比例的显示是不同的.2x就是普通的dpr为1的屏幕使用的; 对于高清屏幕就是用3x,dpr为2或者以上; 2x和3x就是尺寸的大小,2x的图片比3x的小
c)满减图标在export default中create()内使用
classMap=['discount'...]
建立,在标签中经过
:class="classMap[seller.supports[0].type]"
引用。
d)背景模糊:整个header组件中有模糊背景图,可经过建立一个div而后绝对定位至相关位置,再用z-index=-1设置后,使用
filter:blur(15px)增长模糊效果。
e)css sticky footers模块:简而言之就是底部有一个固定图标,只有移动至底部区域才展现,不然不显示的一种布局。
父级 position:fixed,内容设 为padding-bottom:64px,页脚相对定位,margin-top:-64px,clear:both
为了保证兼容性,父级要清除浮动
坑2:新旧版vue-router不一样,旧版支持router.map(),新版则使用const router=new VueRouter()直接在里面写便可
g)flex布局:
意为"弹性布局",用来为盒状模型提供最大的灵活性。设为Flex布局之后,子元素的float
、clear
和vertical-align
属性将失效。如下六个属性设置在容器上:
flex-direction 容器内项目的排列方向(默认横向排列)。可选值为row(默认)沿水平主轴由左向右排列、row-reverse沿水平主轴由右向左排列、column沿垂直主轴右上到下和column-reverse。
flex-wrap 换行方式。nowrap(默认)不换行、wrap换行(第一行在上方)和wrap-reverse
flex-flow 以上两个属性的简写方式 .box { flex-flow: <flex-direction> || <flex-wrap>;}将上述两种方法的值用||链接便可
justify-content 主轴上的对齐方式。主轴到底是哪一个轴要看属性flex-direction的设置了flex-start:在主轴上由左或者上开始排列;flex-end:在主轴上由右或者下开始排列;center:在主轴上居中排列;space-between:在主轴上左右两端或者上下两端开始排列;space-around:每一个项目两侧的间隔相等。
align-items 交叉轴上如何对齐(纵轴对齐方式)flex-start顶对齐| flex-end底对齐 | center居中 | baseline文字基准线 | stretch撑开整个盒子
align-content 定义了多根轴线的对齐方式。若是项目只有一根轴线,该属性不起做用。
设置在子元素的属性也有六个:
order:项目排列顺序,数值越小排名靠前,默认为0;
flex-grow:项目放大比例,默认0;
flex-shrink:缩小比例,默认1;
flex-basis:计算主轴是否有多余空间,默认auto,项目原本大小;
flex:是上面三个简写,默认0 1 auto;
align-self:容许单个项目有与其余项目不同的对齐方式。
f)自适应布局
左侧宽度固定,右侧宽度自适应
// 左侧固定width:80px,右侧自适应 parent: display:flex; child-left: flex:0 0 80px child-right: flex:1
五、goods模块
a)采用better-scroll实现滚动,在package.json代表版本,使用npm安装。BScroll接受两个参数,第一个是DOM对象,第二个是方式,在method中写方法,须要设置 click:true,不然移动端滑动无效。
注意:vue提供一种方法获取DOM,在须要获取地方添加ref="**",而后在js中经过this.$ref.**来引用;
DOM真正映射实在nextTick后,所以一些计算属性应该写在里面,不然在页面中无响应;
六、seller组件
a)商家实景图片横向滚动:
解决方案:每一个 li 要 display:inline-block,由于width不会自动撑开父级ul,因此须要将计算后的宽度赋值给ul的width,(每一张图片的width+margin)*图片数量-一个margin,由于最后一张图片没有margin,同时new BScroll里面要设置scrollX: true, eventPassthrough: 'vertical', // 滚动方向横向
b)打开seller页面,没法滚动
问题分析:出现这种现象是由于better-scroll插件是严格基于DOM的,数据是采用异步传输的,页面刚打开,DOM并无被渲染,因此,要确保DOM渲染了,才能使用 better-scroll;解决方案:用到mounted钩子函数,同时必须搭配this.$nextTick()
c)刷新后,没法滚动
问题分析:出现这种状况是由于mounted函数在整个生命周期中只会只行一次;解决方案:使用watch方法监控数据变化,并执行滚动函数 this._initScroll();this._initPicScroll();
d)缓存数据
使用window.localstorage保存舍设置缓存信息,封装在store.js中
e)axios
在vue1.x的时候,vue的官方推荐HTTP请求工具是vue-resource,可是在vue2.0的时候将推荐工具改为了axios。若是想像之前使用 vue-resource 那样 this.$http.get 调用,要这样定义:Vue.prototype.$http=axios;
经过 this.$http.get 来定义经过vue实例来发送get请求,而后经过then后面的回调函数将请求成功的数据接收,经过状态码来判断是否成功以及复制给vue的数据对象。因为这里是用的mock数据(模拟后台数据),因此用的模拟状态码。
const ERR_OK = 0;//表示没有错误信息,即获取数据成功 this.$http.get('/api/seller').then((response) => { response = response.data; if (response.errno === ERR_OK) { this.seller = Object.assign({}, this.seller, response.data); } });
vue-resource详解:
vue-resource是Vue.js的一款插件,它能够经过XMLHttpRequest或JSONP发起请求并处理响应。也就是说,$.ajax能作的事情,vue-resource插件同样也能作到,并且vue-resource的API更为简洁。
优势:体积小;支持主流浏览器;支持PromiseAPI和URI Templates;支持拦截器。
使用:
首先引入vue-resourec;而后发送请求便可
import VueResource from 'vue-resource'; Vue.use(VueResource); this.$http.get('/someUrl',jsonData).then(function(response){ //jsonData是传给后端的数据 // 响应成功回调 }, function(response){ // 响应错误回调 });
下面给个实例,从a.txt取数据
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> <script src="http://unpkg.com/vue/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/vue.resource/1.0.3/vue-resource.min.js"></script> <script type="text/javascript"> window.onload = function(){ var vm = new Vue({ el:'#box', data:{ msg:'Hello World!', }, methods:{ get:function(){ //发送get请求 this.$http.get('a.txt').then(function(res){ alert(res.body); },function(){ console.log('请求失败处理'); }); } } }); } </script> </head> <body> <div id="box"> <input type="button" @click="get()" value="按钮"> </div> </body> </html>
最新版Vue推荐使用axio,两个功能相似,首先也要安装而后引入,最后再使用:(注:他的注册不能使用Vue.use()方法)
import axios from "axios"; //注册 Vue.prototype.$axios = axios; //使用axios created:function(){ this.$axios.get("/seller",{"id":123}).then(res=>{ console.log(res.data); }); }
f)组件间通信
父传子: props
// 父组件 <v-header :seller="seller"></v-header> // 子组件 header.vue props: { seller: { type: Object } }
子传父: $emit 若是是子组件想传递数据给父组件,须要派发自定义事件,使用 $emit 派发,父组件使用v-on接收监控(v-on能够简写成@)
// 子组件 RatingSelect.vue,派发自定义事件isContent,将this.onlyContent数据传给父级 this.$emit('isContent', this.onlyContent); this.$emit('selRatings', this.selectType); // 父组件 foodInfo.vue 在子组件的模板标签里,使用v-on监控isContent传过来的数据 <v-ratingselect @selRatings="filterRatings" @isContent="iscontent"></v-ratingselect>
非父子组件之间通讯: 1. 大型项目能够用 Vue官方推荐的vuex;event bus: 利用一个中间组件来做为信息传递中介;
g)better-scroll
better-scroll是一个移动端滚动的解决方案,基于iscroll的重写(多年没人维护了,有不少bug;有些状况是基于js实现的帧动画,体验差,bs基于css3等)
使用:首先应有一个父容器div,固定高度且overflow:hidden,给它设置bscroll,子元素高度由内容撑开。
<template> <div class="wrapper" ref="wrapper">//主div <ul class="content"> //子元素 <li>...</li> <li>...</li> ... </ul> </div> </template> <script> import BScroll from 'better-scroll' export default { mounted() { this.$nextTick(() => { this.scroll = new Bscroll(this.$refs.wrapper, {}) }) } } </script>
***重要:在mounted钩子函数里,this.$nextTick的回调函数中初始化better-scroll。是由于wrapper的DOM在这个时候已经渲染了,能够正确计算其高度。
异步数据处理
<template> <div class="wrapper" ref="wrapper"> <ul class="content"> <li v-for="item in data">{{item}}</li> </ul> </div> </template> <script> import BScroll from 'better-scroll' export default { data() { return { data: [] } }, created() { requestData().then((res) => { //requestData()是伪代码,实际用axios活vue-resource this.data = res.data this.$nextTick(() => { this.scroll = new Bscroll(this.$refs.wrapper, {}) }) }) } } </script>
初始化bs须要在数据获取以后。之因此放在created而不是mounted,是由于requestData是一个异步过程,拿到相应数据时,DOM已经渲染好,可是,数据改变后到DOM从新渲染仍然是异步过程,因此仍是要异步初始化bs(nextTick)。
注意:在PC上,点击事件会执行两次。因为better-scroll派发的事件有event_constructed:true属性。能够进行处理。
if (!event._constructed) { return; }
七、sass
Sass的学名叫“CSS预处理器”,就是在CSS的基础上,引入了变量、嵌套、mixin(混合)、运算以及函数等功能,增长了代码的灵活性,可让咱们以更少的代码实现一样的效果,并且代码的整洁度、可读性更强。
.scss是Sass3引入的新语法,基本写法与CSS大体相同
基本语法:
a)变量。css属性的值(1px,bold)均可替换为变量。
$box-color: red; //定义变量 ul{ color: $box-color; //引用 } li{ background-color: $box-color; //引用 }
b)嵌套
/***********************************************选择器嵌套*******************************************/ div { h1 { color: #333; } p { margin-bottom: 1.4px; a { color: #999; } :hover{ color: #888; } } } /* 编译后 */ div h1 { color: #333; } div p { margin-bottom: 1.4px; } div p a { color: #999; } div p:hover{ color: #888; } /***********************************************属性嵌套*******************************************/ div { border: { style: solid; width: 1px; color: #ccc; } } //编译后 div { border-style: solid; border-width: 1px; border-color: #ccc; }
c)继承:使用选择器的继承,要使用关键词@extend,后面紧跟须要继承的选择器。
.class1 { border: 1px solid #333; } .class2 { @extend .class1; background-color: #999; } //编译后 .class1, .class2 { border: 1px solid #333; } .class2 { background-color: #999; }
d)Mixin混合器:使用@mixin声明,经过@include minxin名称调用
@mixin mixName { float: left; margin-left: 10px; } div { @include mixName; } //编译后: div { float: left; margin-left: 10px; } /*带参数的声明及调用*/ @mixin left($value: 10px) { float: left; margin-left: $value; } div { @include left(66px); } //编译后: div { float: left; margin-left: 66px; }
e)颜色函数
$box-color: red; li{ background-color: darken($box-color,30%); } //编译后 li{ background-color: #660000; } //更多 lighten(#cc3,10%)//#d6d65c grayscale(#cc3)//#808080 complement(#cc3)//#33c
f)导入:@import "地址"; 如@import "../../common/scss/mixin.scss";
八、优化:
每次切换模块后,都会从新渲染DOM,解决方法为给APP.vue下的<vue-router 加入keep-alive></vue-router>,组建的切换状态就会保留。
九、打包:
使用npm run build打包文件,其执行了node build/build.js脚本,最终产生一个dist目录,里面包含css,js,index.html文件,打包后文件经过HTTP server启动(sell总目录下建立一个produ.server.js写完代码后使用node prode.server.js启动)
技巧:(1).若是要使用new建立对象,因为本项目使用了ESlint检查代码规范,所以会报错,在这里使用/*eslint-disable no-new*/来跳过校验;
(2). 在build->webpack.base.conf.js中,module.exports下的resolve的alias下能够定义路径,就不用每次引用时加./表示当前路径 等相似问题了;
(3).图标与文字不齐,可设置vertical-align:top来对齐,还不行能够对图片设置paddingtop。
(4).手机测试网页技巧:将localhost换成本身的ip,而后复制地址栏地址,进入草料二维码,而后生成二维码,而后用手机扫一扫就能够查看了,前提是,你手机和电脑必须在同一个局域网。