(给达达前端加星标,提升前端技能)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQmo3V010UU9OTkRjOXBDTWpiUFJObG02QjZlaEFxN2d4SEZpYlhpYnFYMzFyYUlLa242NE1Ra3NpY2ljTzlpYmhqaWJsaWFXVGo3MmlhWFJwVnNBLzY0MA?x-oss-process=image/format,png)
开发一款vue.js开发一款app,使用vue.js是一款高效的mvvm框架,它轻量,高效,组件化,数据驱动等功能便于开发。使用vue.js开发移动端app,学会使用组件化,模块化的开发方式。
学习了如何根据需求分析开发,使用脚手架工具,数据mock,架构设计,自己测试,编译打包等流程。
线上生产环境,如何考虑架构设计,组件抽象,模块拆分,代码风格统一,变量命名要求规范等优点。
一款外卖app,商家页面,商家基本信息(顶部),商品区块,商品列表,分类列表,小球飞入购物车的动画。商品详情页,需要有顶部商品的大图,商品的详细信息,以及还有商品的评价列表。
商品,评论列表,商家展示商家的详情信息。
用vue-resource与后端做数据交互,vue-router前端路由,better-scroll的Js库等。使用vue-cli脚手架,搭建基本代码框架,vue-router官方插件管理路由。vue-resource是用于ajax通信的,webpack构建工具的使用。
Vue是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。
Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,Vue.js 自身不是一个全能框架——它只聚焦于视图层。因此它非常容易学习,非常容易与其它库或已有项目整合。
目录/文件说明build项目构建(webpack)相关代码config配置目录,包括端口号等。我们初学可以使用默认的。node_modulesnpm 加载的项目依赖模块src包含了几个目录及文件:
assets: 放置一些图片,如logo等。 components: 目录里面放了一个组件文件,可以不用。 App.vue: 项目入口文件,我们也可以直接将组件写这里,而不使用 components 目录。 main.js: 项目的核心文件。 static静态资源目录,如图片、字体等。test初始测试目录,可删除.xxxx文件这些是一些配置文件,包括语法配置,git配置等。index.html首页入口文件,你可以添加一些 meta 信息或统计代码啥的。package.json项目配置文件。README.md项目的说明文档,markdown 格式
说一说mvc和mvvm的区别
mvc的全名是Model view Controller,是模型model,视图view,控制器controller的缩写,用一种业务逻辑,数据,界面显示分离的方法来写代码,view视图,视图层调用控制器到controller控制器,控制器调用model,model返回数据给控制器,然后控制器将数据返回给view。
这是mvc的简单调用流程,mvc模式是单向的数据绑定,view视图层调用model层,要通过中间层controller来实现。
mvvm模式是双向数据绑定,view,model,vm进行数据的绑定和事件的监听,对view和model进行监听,当有一方的值发生变化时,就更新另一个。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQmo3V010UU9OTkRjOXBDTWpiUFJObGljbWxaemJUQ2gzelhTeWRwd3hNSFFZWjNTMWdJaWJveW5vVzNycDJld09QSWhFM3hQbzM3NnBBLzY0MA?x-oss-process=image/format,png)
数据响应原理
组件化原理
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQmo3V010UU9OTkRjOXBDTWpiUFJObDg5bjlHek5BWU5NYWZMcWJFZklUWGt0V0J2U2pZY2hqeWcwdk14U2NKekhDUjBOR2RUN0dxUS82NDA?x-oss-process=image/format,png)
vue-cli,vue.js的开发利器,脚手架
vue-cli可以搞定,目录结构,本地调试,代码部署,热加载,单元测试。
vue-cli的安装方法:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQmo3V010UU9OTkRjOXBDTWpiUFJObDdJV1p1ajVZd3IzRlFvNEttQ2tvaHlySElrQnlpYTREWVZ6RGlhemlhemVVUW1pY3JyVWdGZDJaQkEvNjQw?x-oss-process=image/format,png)
node -v
mac
sudo npm install -g vue-cli
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQmo3V010UU9OTkRjOXBDTWpiUFJObEE5WHZPc3V3TndSNHEyRk1UcndLbkdmaWNyOFZsaWJjRUkyUWcycGthZlJLTWdqbHNoeXJwOVJRLzY0MA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQmo3V010UU9OTkRjOXBDTWpiUFJObGhqa1dXSUFldVZiZExSVFVwMUNscmlhREtZUk8za2dvaWFOV01ZcjVxODFPV0RpY2U1V3ZpYVI1NHcvNjQw?x-oss-process=image/format,png)
使用webpack模板,名字sell,外卖app。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGFHaWJHTFZSTG8yQzR4Z29RbEY5WVhrR2dMRnppY2F3d1Z6RjdnSjI5OUF2MmdpYTRZTXlFR3hyZy82NDA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SHRVR29HQXI4bnBjVUZjYlN1Rnk2WU1LM2ZBM2tQaWJzNkhycXFuU2VyRExsanZXU3dLS004OVEvNjQw?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SDBwclVCaWJoVlJlSHdjM0UxMFI2WG0zRGtSZHF4aWFZSm9KaWFwYjFuaWJKWDN0b2M0a2lhOHNpYUJhQS82NDA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGNjQ3NpYWFJTEhnM2ViNmRORjhhOXYzS3BkZGRsT2QzRWZEODAwNXBGR2liaGpEY3Fib2ljZzNYdy82NDA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGhvYlZmNHN5aDFBWkV3RkpMUUw2b2FUc1NpY0tuSmNBU21hUzZKaWJkQjFpYTVjYWdsU21EV2thQS82NDA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SExLZGxaYVpTRlBtcmNRUlBobUJZSzBEN2liWWZTbXhpY2RaTk5seUo5aWFXSmUyaWE3a2NnRVFpYWhRLzY0MA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SDY1YVA1Y1p6dmlicEFWSTVEVnJwdDFMMmp6aWNXZmNNbmljMmJHU0lDWG93RGhzV2xObWNPam40QS82NDA?x-oss-process=image/format,png)
运行效果:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SEMyZjdGUW0xVm9XNDVpY1pKWTJocmhSQlplTmdJUVFuWVpyNklpYnZYVkRPQWJLaERYZDZITlpBLzY0MA?x-oss-process=image/format,png)
然后把项目放进你的编辑器
mode_modules文件夹:npm install 安装的依赖代码库
src文件夹是我们存放的源码
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SEhnTEpEVTVqS0ZUNzFkMnhZUjU5VWJ4YTlpYmMxV2FIa0ZXaWFBM1ZHczllUE9XTEdtTDR5QVdRLzY0MA?x-oss-process=image/format,png)
这个文件跟我不一样也没事。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SG1OY2gwMWdhb0w3ZmJVblBTSENwWnlnZmN2VDcyblRBZ3J3R3JOWm1sRnR5SlFOZFNhR0JnQS82NDA?x-oss-process=image/format,png)
editorconfig是编辑器的配置
eslintignore为忽略语法检查的目录文件
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGljRko2aEZIbE9pYm1HWXZPTGxkTXRvaWE1bWc2aWJjaFUxcGZvaWFVU3VlbGlhb0U2TlFmMkdheEhPQS82NDA?x-oss-process=image/format,png)
eslintrc.js为eslint的配置文件
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SG5FeVlKNDZmdmVWb25OWWlhYmljNTRpYnE5RW9wZDNTMzY3RjE4T3hxSHhLSUp6bXlSYTFTQVBSZy82NDA?x-oss-process=image/format,png)
商品页面:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGtnTE5SY00ybGtjbWpjOXY2MzRZUXJtR2dCTmRWczdEaWFpYjdRcnNwTHhCRFU3aWFwSmljeFlOVVEvNjQw?x-oss-process=image/format,png)
商品页_公共以及优惠信息
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SG5PRDA3OU5zZHZOaWIzM1B3elN6SjJGN2JQTnA4NXBZWlpZYTBCTFo1MGljSHRGZUpiNWRNT1RBLzY0MA?x-oss-process=image/format,png)
商品页购物车详情
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SEIzRWtNbGN2RGVlalAyVjVPdWppYXpEMVZpYjVsRFVyOVpCcDBuYW9aQ0NpYUtjVkdXN2RpYjB1OXcvNjQw?x-oss-process=image/format,png)
商品页面_商品详情页面
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SDBvM2s3VnZHaWNzclZUZlhtQll2MGprUmlhdmd0dzFqQnNyckt6cU91TTNmeHJ4cnZ6MHZNMmFnLzY0MA?x-oss-process=image/format,png)
评价页
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGljUE9WSVk5WUFUVVJ6MFV1VFdIMmlhNUlLRUVTNDBwelRueFBTQVZ4TGwwcWIwM2tUZ3I3U1ZBLzY0MA?x-oss-process=image/format,png)
商家页
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGljb2Fzem5sMExTaWJlNGljcFdzNXYzT29ZZDdpYVNTQ01pYXppY083bWd2bVNkYmR4Y2EwekV1c2pxZy82NDA?x-oss-process=image/format,png)
设备像素比devicePixelRatio
在移动端,devicePixelRatio指的是window.devicePixelRatio。
移动端设备分为非视网膜屏幕和视网膜屏幕。
window.devicePixelRatio是设备上物理像素和设备独立像素的比例,公式表就是:window.devicePixeRatio = 物理像素/dips。
icomoon.io,图标字体制作
mock数据,模拟后台数据
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGQwZUZMOUc1NWpKU2xtZDQ1SExPSlhGRzJHMzU0aWJpY29wQ2xsTDVTaWE3d0VGcGRONXRlNG1Ndy82NDA?x-oss-process=image/format,png)
icon- 开头的图标(如图所示)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SEFmOXJCcklqVjhpYXk1YXdZTXFBclFNaHBra0tTRVVhRjRVVnIxelRvWFRTYnJ3MzlycWliRlFnLzY0MA?x-oss-process=image/format,png)
首先进入网页https://icomoon.io/
然后点击右上角的“IcoMoon APP”按钮,选择导入自己的SVG图来生成ico-的图标,点击新页面左上角的“Inport ICONS”。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SE1ZODFxNm0wRDRLbld6RWM4Q0VHaWJpY0NRZFpiaWJzcnZrUG1ubzlSSllVcmxTMnA2aldtcmI1US82NDA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SGljYmxhM21QYTN0SlNWakppYWV1emcyWFFMZktMN3ZKaWFOalpxYW5wUEdkdGljVXk3bTNpYmw0dVJ3LzY0MA?x-oss-process=image/format,png)
在devServer下面加入
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQ3hVVktMb252dENNNDBTd3NMNWd5SEFZVUZuQmZFTVJpYXVhaWJVVjlWeDg1RWliU0hKMXI1dDZ2N2YxaWFrSkYxNUg4T040RHdBNlpySUEvNjQw?x-oss-process=image/format,png)
页面骨架开发
sell->build->confi->node_modules->resource, img, psd, svg ->src, common->components, app.vue->static
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFERHNqcUozdzNFTWljQUVsWUN1ZXlBaWJza0t3dlM4RVVCYkJUOW5walEwYXVwUmlhcVlkT1BRdTZDYW1ZNlpwVzZvMDVGM2RaM0k1VW13dy82NDA?x-oss-process=image/format,png)
sell
meta name="viewport"
它是移动端浏览器在一个比屏幕更宽的虚拟窗口中渲染页面,用来实现展示没有做移动端适配的网页,可以完整的展示给用户,viewport的宽度就是可显示区域的宽度。
这些属性可以混合使用,width控制视图窗口的宽度,height控制视图窗口的高度,这个属性很少用,initial-scale为控制页面最初加载时在最理想的情况下缩放的等级,通常设置为1.0,可以是小数,maximum-scale为允许用户的最大缩放量,minimum-scale为允许用户的最小缩放量。
user-scalable为是否允许用户进行缩放,值只能“no”或者“yes”。no为不允许,yes为允许。
width和initial-scale设置了两者,浏览器会自动选择数值最大的进行适配。
就是当窗口的最适配理想宽度为300时,initial-scale的值设置为1时,width设置的值为400,那么取最大值,400。
当窗口的最适配理想值为500时,那么取的值为500。
width=device-width和initial-scale=1都表示为最理想的viewport,但是在ipad,iphone等移动设备,ie上,横竖屏不分,默认都为竖屏的宽度,兼容的最好写法。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFERHNqcUozdzNFTWljQUVsWUN1ZXlBaWJzZlNqRUFOcUU4WTNwaWFQRXQyRFZaczFEdW91RGYyb0xhUE1LN1VlS2o5eElSbTAwZXNYc1dwUS82NDA?x-oss-process=image/format,png)
什么是viewport,它是用户网页的可视区域,翻译就是视区。
手机浏览器是把页面放在一个虚拟的"窗口"(viewport)中,通常这个虚拟的"窗口"(viewport)比屏幕宽,这样就不用把每个网页挤到很小的窗口中(这样会破坏没有针对手机浏览器优化的网页的布局),用户可以通过平移和缩放来看网页的不同部分。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVmpUTlhDZVU4UVpLSXRwc1ZTWUlrTmNIeVRqYUxEc2M3dHRreE9pY2lhSDhmZVYyQUxpYWVkbzhOUS82NDA?x-oss-process=image/format,png)
没有添加viewport的效果:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVnl6aWFSdExlNlVwTlRnYWhLaWJpYVdrbVNpYVdhOGliakxDa29wbGRpY2lheVNSU3NqajU5YVNRYng4bkEvNjQw?x-oss-process=image/format,png)
加了viewport的效果:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVllLeW01c0cyamJpY1BRblhzaWE0d0ZnbVAyRno3Y3U1ekE1VGlhNWUzUzM4bUJnV3F5NUxIOEs4QS82NDA?x-oss-process=image/format,png)
viewport这个特性被用于移动设备,但是也可以用在支持类似“固定到边缘”等特性的桌面浏览器,如微软的edge。
按百分比计算尺寸的时候,就是参照的初始视口,它指的是任何用户代理和样式对它进行修改之前的视口。桌面浏览器如果不是全屏模式的话,一般是基于窗口大小。
在移动设备上,初始视口通常就是应用程序可以使用的屏幕部分。
在viewport中就是浏览器上用来显示网页的那部分区域。
width=device-width能使所有浏览器当前的viewport宽度变成理想的宽度,initial-scale=1是将页面的初始缩放值设置为1。用来将viewport的宽度变成为理想的宽度,防止横向滚动条出现。
width=device-width表示为宽度是设备屏幕的宽度
initial-scale=1.0表示为初始的缩放比例
minimum-scale=0.5表示为最小的缩放比例
maximum-scale=2.0表示为最大的缩放比例
user-scalable=yes表示用户是否可以调整缩放比例
设备像素,设备独立像素,css像素掌握
设备像素就是屏幕上的真实像素点,iphone6的设备像素像素为750*1334,则屏幕上有750*1334个像素点;设备独立像素,操作系统定义的一种长度单位,iphone6的设备独立像素375*667,正好是设备像素的一半,css像素,css中的长度单位,在css中使用px都是指css像素。
物理像素来代表设备像素,独立像素代表设备独立像素。
在很早的时候,只有物理像素,没有独立像素,在不缩放的前提,css中的1px代表着一个物理像素。
不过从iphone4开始,推出了retina屏幕,物理像素变成640*960,屏幕尺寸没有变化,在单位面积上的物理像素的数量增加了,则表示屏幕密度增加了。按照原来,1px css像素由1个物理像素来渲染,那么width:320px的元素就会占据半个屏幕的宽度。
1个独立像素==2个物理像素
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6LzNhUGowR2hGUURCWG5RUEVoenFTNVVaTUZUZVJUeExWcmlhV1lId1dqOGVGVnBScHllOG9XOEl2d250WTBaUlhJTWVQM0FlVU9Ib0xWekhzb2pXU0dHZy82NDA?x-oss-process=image/format,png)
viewport是浏览器窗口,代表浏览器的可视区域,就是浏览器中用来显示网页的部分区域。
像素单位有设备像素,逻辑像素,css像素。
设备像素也叫物理像素。
什么是设备像素,它指的是显示器上的真实像素,每个像素的大小是屏幕固有的属性。
设备分辨率是用来描述这个显示器的宽和高分别有多少个设备像素。
设备像素和设备分辨率由操作系统来管理。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVlN4VlVQNm5vRzdMRVBWdGliR053dlB2OWlicnZtWmZOTGVyS0dpYmFXYlJ2dmNwY2lhOUVnNFJnZ2cvNjQw?x-oss-process=image/format,png)
全局安装vue-cli脚手架工具
cnpm install -g vue-cli
初始化sell项目
vue init webpack sell
进入sell目录
cd sell
安装依赖
cnpm install
运行项目
cnpm run dev 或者 node build/dev-server.js
写mock数据接口
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVjhta0tlZXlnNFZpY2ljNWd0T09pY3c3ZU56Rmp6VVRLUG5RdHljTXFjeHFiRmtlbXlISTE1b21sdy82NDA?x-oss-process=image/format,png)
// 文件位置:build/dev-server.js
// 注:此处是关键代码
var app = express()
var appData = require('../data.json')
var seller = appData.seller
var goods = appData.goods
var ratings = appData.ratings
var apiRoutes = express.Router()
apiRoutes.get('/seller', function (req, res) {
res.json({
error: 0,
data: seller
})
})
apiRoutes.get('/goods', function (req, res) {
res.json({
error: 0,
data: goods
})
})
apiRoutes.get('/ratings', function (req, res) {
res.json({
error: 0,
data: ratings
})
})
app.use('/api', apiRoutes)
项目实战,页面骨架开发
webstorm设置文件的默认结构
export default {}
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVlRnaWJ0RWRTSEhwTGVvTHJpY1l1a3dsMkx0Z0NvemliOEp6bWlhaWJsNFdNeXNNczlnQkVsN2V1MFJBLzY0MA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVmt2OWliam9DeFpLZmljQVVpYUJ2Mk5KRGt6TlJZaGtLOGNDMzV3cXBJQXptdjI4ZU9Db2pPRGlhaWJ3LzY0MA?x-oss-process=image/format,png)
安装ajax异步请求插件vue-resource
cnpm install vue-resource --save-dev
文件位置:src/APP.vue
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVkNBQ0hlZXJZdllveUJ1VVBuV1Qwd1lzVXBvcHFFZm1uZzhuWXJxZmxwanNNMFZGakdVSkRBZy82NDA?x-oss-process=image/format,png)
商品
评论
商家
import {urlParse} from './common/js/util';
import header from './components/header/header.vue';
const ERR_OK = 0;
export default {
data() {
return {
seller: {
id: (() => {
let queryParam = urlParse();
return queryParam.id;
})()
}
}
},
created() {
this.$http.get('/api/seller?id=' + this.seller.id).then(response => {
response = response.body;
if (response.error === ERR_OK) {
this.seller = Object.assign({}, this.seller, response.data);
console.log(this.seller.id);
}
}, response => {
});
},
components: {
'v-header': header
}
}
@import "common/stylus/mixin.styl"
.tab
display: flex
width: 100%
height: 40px
border-1px(rgba(7, 17, 27, 0.1))
line-height: 40px
.tab-item
flex: 1
text-align: center
& > a
display: block
font-size: 14px
color: rgb(77, 85, 93)
&.active
color: rgb(240, 20, 20)
文件位置:src/router/index.js
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVnBjaWJHS0x5NTVKd2RqNDhld01waDBrTVR5RG1ncUZHcHpXZDlaRFNwdDVMbXR1a2t4dEpEbncvNjQw?x-oss-process=image/format,png)
import Vue from 'vue';
import Router from 'vue-router';
import goods from '@/components/goods/goods.vue';
import ratings from '@/components/ratings/ratings.vue';
import seller from '@/components/seller/seller.vue';
Vue.use(Router);
const routes = [{
path: '/',
component: goods
}, {
path: '/goods',
component: goods
}, {
path: '/ratings',
component: ratings
}, {
path: '/seller',
component: seller
}];
export default new Router({
linkActiveClass: 'active',
routes: routes
});
文件位置:src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import VueResource from 'vue-resource';
Vue.config.productionTip = false;
import '../static/css/reset.css';
import './common/stylus/base.styl';
import './common/stylus/index.styl';
import './common/stylus/icon.styl';
Vue.use(VueResource);
new Vue({
el: '#app',
router,
render: h => h(App)
});
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVmdka2VubE9WMUhFeHdrWlp4c3dpY3FFMWVoSmFWd2VCRUIyNVJhY1JUQXl1N1dxUmdpYVo0OFRnLzY0MA?x-oss-process=image/format,png)
安装better-scroll
cnpm install better-scroll --save-dev
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVjRhcnc2bmhpYWNTMFl4MXhRYzhkektpYUtzd3l1aGwxUEMxeUd5ZHpobEprandXa3JYWURNN013LzY0MA?x-oss-process=image/format,png)
export default {
created() {
this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee'];
this.$http.get('/api/goods').then(response => {
response = response.body;
if (response.error === ERR_OK) {
this.goods = response.data;
console.log(this.goods);
this.$nextTick(() => {
this._initScroll();
this._calculateHeight();
})
}
}, response => {
});
}
}
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVlMwcU5xaWJSekQ1WlJyRWhDNEQ0dHVGZUFDcWM4cWliYjI3aFRhMThvQ1owaG5leTdRZWlhMm9KUS82NDA?x-oss-process=image/format,png)
export default {
methods: {
selectMenu(index, event) {
if (!event._constructed) {
return;
}
console.log(index);
let foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');
let el = foodList[index];
this.foodsScroll.scrollToElement(el, 300);
},
_initScroll() {
this.menuScroll = new BScroll(this.$refs.menuWrapper, {
click: true
});
this.foodsScroll = new BScroll(this.$refs.foodsWrapper, {
click: true,
probeType: 3
});
this.foodsScroll.on('scroll', (pos) => {
this.scrollY = Math.abs(Math.round(pos.y));
})
},
_calculateHeight() {
let foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');
let height = 0;
this.listHeight.push(height);
for (let i = 0; i < foodList.length; i++) {
let item = foodList[i];
height += item.clientHeight;
this.listHeight.push(height);
}
}
},
components: {
shopcart,
cartcontrol
}
}
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVlI0d1lUYmFPcllpY2FnbFo1QzAzODhBaWNpY1VmaWE3Y0VadHcwQTFiQTNzSmhpY0ZXMFc1MTN2cDB3LzY0MA?x-oss-process=image/format,png)
Vue.set(this.food, 'count', 1);
小球动画函数监听
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVlRVU3B0ZmZJQ0hoazZYcGQ2aGxVRFlQR2NSaWNoQkpWZjVZdm5INnI4Z1l3anRrSXpic0xkY1EvNjQw?x-oss-process=image/format,png)
export default {
methods: {
drop(el) {
for (let i = 0; i < this.balls.length; i++) {
let ball = this.balls[i];
if (!ball.show) {
ball.show = true;
ball.el = el;
this.dropBalls.push(ball);
return;
}
}
},
beforeDrop: function (el) {
let count = this.balls.length;
while (count--) {
let ball = this.balls[count];
if (ball.show) {
let rect = ball.el.getBoundingClientRect();
let x = rect.left - 32;
let y = -(window.innerHeight - rect.top - 22);
el.style.display = '';
el.style.webkitTransform = `translate3d(0,${y}px,0)`;
el.style.transform = `translate3d(0,${y}px,0)`;
let inner = el.getElementsByClassName('inner-hook')[0];
inner.style.webkitTransform = `translate3d(${x}px,0,0)`;
inner.style.transform = `translate3d(${x}px,0,0)`;
console.log(el, x, y);
}
}
},
dropping: function (el, done) {
let rf = el.offsetHeight;
this.$nextTick(() => {
el.style.display = '';
el.style.webkitTransform = 'translate3d(0,0,0)';
el.style.transform = 'translate3d(0,0,0)';
let inner = el.getElementsByClassName('inner-hook')[0];
inner.style.webkitTransform = 'translate3d(0,0,0)';
inner.style.transform = 'translate3d(0,0,0)';
el.addEventListener('transitionend', done);
});
},
afterDrop: function (el) {
let ball = this.dropBalls.shift();
if (ball) {
ball.show = false;
el.style.display = 'none';
}
}
}
}
文件位置:src/common/js/date.js
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVjVaZHJoM1RWaWFpYjBhaWNNYlNjOXVzdlhBdnN6UE9NTlJJYWNIUlhzMFdaQ2UxclM3ZzVNSVd1QS82NDA?x-oss-process=image/format,png)
export function formatDate(date, fmt) {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
}
}
return fmt;
}
function padLeftZero(str) {
return ('00' + str).substr(str.length);
}
import {formatDate} from '../../common/js/date';
filters: {
formatDate(time) {
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm');
}
}
}
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVmlidWtWSG5Cdk02VGZXalZxZ2dvd2ljWnRMeUhPaWNTVW9iNGlhVUY0bWM3WTNmUm5yd0RVbFlGaWJBLzY0MA?x-oss-process=image/format,png)
export default {
mounted() {
console.log('mounted');
this._initScroll();
this._initPics();
},
updated() {
console.log('updated');
this._initScroll();
this._initPics();
}
}
本地存储相关操作封装
文件位置:src/common/js/store.js
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVlpjUVpLQXl3eEF2QWpqQ1N4UmliaWNGRUltYk91RFNVN3I2d1ZjYzhuR2lhWkIwd1lHdDh4VkNYdy82NDA?x-oss-process=image/format,png)
// 存储到本地存储
export function saveToLocal(id, key, value) {
let seller = window.localStorage.__seller__;
if (!seller) {
seller = {};
seller[id] = {};
} else {
seller = JSON.parse(seller);
if (!seller[id]) {
seller[id] = {};
}
}
seller[id][key] = value;
window.localStorage.__seller__ = JSON.stringify(seller);
}
// 从本地存储里面读取
export function loadFromLocal(id, key, def) {
/* eslint-disable semi */
let seller = window.localStorage.__seller__;
if (!seller) {
return def;
}
seller = JSON.parse(seller)[id];
if (!seller) {
return def;
}
let ret = seller[key];
return ret || def;
}
解析url参数
文件位置: src/common/js/util.js
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVlhEYktwaWE2WUZoR0VGcHl0dkZIT0pwWktUUElmb3RsRGdhWEs1QmliRnZjZTdDV2tCTmJaQkpBLzY0MA?x-oss-process=image/format,png)
export function urlParse() {
let url = window.location.search;
let obj = {};
let reg = /[?&][^?&]+=[^?&]+/g;
let arr = url.match(reg);
if (arr) {
arr.forEach((item) => {
let tempArr = item.substring(1).split('=');
let key = decodeURIComponent(tempArr[0]);
let val = decodeURIComponent(tempArr[1]);
obj[key] = val;
})
}
return obj;
}
项目编译打包
cnpm run build
配置打包规范:config/index.js
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVjRFbVBGakdlUFNiTHhvRUZ4aGREaWFFN2ljam5kck9pY1dyTTJLejhlUlNyV0FVakh6dGliaWFJNE5nLzY0MA?x-oss-process=image/format,png)
module.exports = {
build: {
productionSourceMap: true,
port: 9000
},
dev: {
}
}
利用express编写一个本地服务器
文件位置:./prod.server.js
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVjAzakw5dTRRd3BISVRwbDlJWUNNbnpHQzBtZVp5Z1hwUGJpYmw4ZWxmUFF5aWI3MmJXWm9US1Z3LzY0MA?x-oss-process=image/format,png)
let express = require('express');
let config = require('./config/index');
let port = process.env.PORT || config.build.port;
let app = express();
let router = express.Router();
router.get('/', function (req, res, next) {
req.url = '/index.html';
next();
});
app.use(router);
let appData = require('./data.json');
let seller = appData.seller;
let goods = appData.goods;
let ratings = appData.ratings;
let apiRoutes = express.Router();
apiRoutes.get('/seller', function (req, res) {
res.json({
error: 0,
data: seller
})
});
apiRoutes.get('/goods', function (req, res) {
res.json({
error: 0,
data: goods
})
});
apiRoutes.get('/ratings', function (req, res) {
res.json({
error: 0,
data: ratings
})
});
app.use('/api', apiRoutes);
app.use(express.static('./dist'));
module.exports = app.listen(port, function (err) {
if (err) {
console.log(err);
return;
}
console.log('Listening at http://localhost:' + port);
});
Eslint规范总体设置
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVnJMaWFxZ0pERGljZm1tUU02aWE3bHRNNGlhVXRmZmszSU9ERVlndVpSdW13Uk9ycGptVXhFSzdaZVEvNjQw?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVlBWT3ROWjB2bGxEaWNwVmtMR2FGYjkxTmtNR1U0bFNpYkQ4WTlKbmJTcnh5eGpZeTVlcHNBbXJnLzY0MA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVmVER2JnUmVhd0FrRGVkc3J2ZHY4NmFPaWNmUlNnaHNVcVRmZlBSaWFRNFZVS25oaWJjUktCQW1UZy82NDA?x-oss-process=image/format,png)
项目开发流程
需求分析,脚手架工具,数据mock,架构设计,代码编写,自测,编译打包。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQlhuUVBFaHpxUzVVWk1GVGVSVHhMVk03WUVpYkJMSzdMc3d2ZlExY0ROYjhvanZUWUF3eUVnekdQekdpYmFVemFGT3NWWnNWcDVFVll3LzY0MA?x-oss-process=image/format,png)
可以看看别人的代码
仿【饿了么】订餐软件的一个demo
https://github.com/guxun12/ele_demo
参考资料&资源
慕课网视频,Vue.js高仿饿了么外卖App
Vue.js 高仿饿了么外卖 App 课程源码,课程地址: http://coding.imooc.com/class/74.html
推荐阅读 点击标题可跳转
【面试Vue全家桶】vue前端交互模式-es7的语法结构?async/await
【面试需要-Vue全家桶】一文带你看透Vue前端路由
【面试需要】掌握JavaScript中的this,call,apply的原理
2019年的每一天日更只为等待她的出现,好好过余生,庆余年 | 掘金年度征文
进来就是一家人【达达前端技术社群⑥】
这是一个有质量,有态度的公众号
点关注,有好运
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8zYVBqMEdoRlFEQnRZVWNJWmdIUzZLRkRBOHZ2dVhHWVZ5VXBpYWJOZzd1U1dOU2RpYmoyVWh6ZVBvNDVBbGU3SlhmeHgxbXVTVnUxdnlLQVcyRFZQQ0p3LzY0MA?x-oss-process=image/format,png)
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9Uc29tRVFBS1A0Y29tcEUzUmljb25aaWJ0V09pYUhxWnQzNDlkUDA0ZTJwUkdGaWFyWVNlYUtlR3BxVk1pY0h6aWFoYlNYb2VkZ09CVEVRWTQySkcycGZPMWQ4US82NDA?x-oss-process=image/format,png)
|