原文https://www.cnblogs.com/ljq66/p/9980372.html
前言:学习Vue.js高仿饿了么课程过程中,总结了这个Web App项目从准备到开发完毕自己觉得很重要的知识点。这一篇主要介绍:项目准备、页面骨架开发、header组件开发。
项目github地址:https://github.com/66Web/ljq_eleme,欢迎Star。
App | header |
一、项目分析&学习目标 |
当前最火的MVVM框架
高仿上线外卖App标准来开发
开发一个webApp的全流程
以线上生产环境的代码质量作标准
功能技术分析
学习目标
学习内容
二、Vue.js介绍 |
近年来前端开发趋势
(前者传统MVC:更新数据会刷新页面 后者前端MV*: 向后端REST API异步请求数据,局部刷新页面)
MV* —— MVC、MVP、MVVM
MVVM框架
View ViewModel Model
视图 通讯 数据
对比Anglar React
vue.js 核心思想
组件设计原则
三、Vue-cli开启Vue项目 |
Vue-cli 是Vue的脚手架工具 —— 帮助写好Vue基础代码的工具
安装使用
1 |
|
1 |
|
项目文件
→ scripts: 配置一些需要执行的命令
→ dependencies:开发环境中的依赖
→ devdependencies: 编译过程中的依赖
项目运行
1 |
|
【小知识点】sublime自动格式化 —— Command+option+L 或 Control+alt+L |
import Hello from './compoments/Hello'
export default{ components: { Hello //es6语法 相当于 'Hello': Hello } }
开发时的Webpack配置与编译
→ 配置默认识别的路径
resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), } }
四、准备工作 |
图标字体制作
项目目录设计
【css的stylus语法】
|
前后端分离
{ "seller":{} //商家相关字段 "goods":{} //商品相关字段 "rattings":{} //评论相关字段 }
webpack.dev.conf.js中配置
const express = require('express')//开启一个node server const app = express() //定义一个对象,包含express返回的数据 var appData = require('../data.json') //定义一个对象引入data数据 var seller = appData.seller; var goods = appData.goods; var ratings = appData.ratings; app.use('/api', apiRoutes); //调用app对象
before(app) { app.get('/api/seller', (req, res) => { res.json({ errno: 0, //错误码:实际上是业务方根据业务自己定的 data: seller }) //接口返回json数据,上面配置的数据seller就赋值给data请求后调用 }), app.get('/api/goods', (req, res) => { res.json({ errno: 0, data: goods }) }), app.get('/api/ratings', (req, res) => { res.json({ errno: 0, data: ratings }) }) }
查看json数据
【Google安装第三方插件】
—— 转载自【小白白打酱油博客】 |
五、页面骨架开发 |
移动端视口
App.vue 中把页面拆为三个区块
headertabcontent
然后,分别抽成一个组件,引用 —— 所有组件自定义标签名不可与html本身标签重合 'v-header': header
移动端经典布局 flex
商品评论商家
.tab display: flex width: 100% height: 40px line-height: 40px .tab-item flex:1 text-align: center
vueRouter
商品
main.js 中设置单页面应用路由的【挂载组件】—— 默认App.vue 也可以自定义组件如layout.vue
/* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '' })
配置【路由map】:router->index.js
export default new Router({ mode: 'history', routes: [ { path: '/', redirect: '/goods',//默认页面重定向 }, { path: '/goods', component: goods }, { path: '/ratings', component: ratings }, ] })
#app .tab .tab-item>a{ display: block; font-size: 14px; color: rgb(77, 85, 93); } #app .tab .tab-item>a.router-link-active{ color: rgb(240, 20, 20) }
1像素border实现
.tab{ border-bottom: 1px solid rgba(7,17,27,0.1) }
【PC开发中用手机实时预览的小技巧】
|
- 定义mixin.styl: 通过css预处理器的函数方法,实现伪类线
border-1px($color) position: relative &:before display: block position: absolute left:0 top: 0 width: 100% border-bottom: 1px solid $color content: '' &:after display: block position: absolute left:0 bottom: 0 width: 100% border-top: 1px solid $color content: ''- 定义base.styl: 实现不同DBR的移动端的缩放
@media(-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5)//DPR为1.5的缩放0.7倍 .border-1px &:before -webkit-transform: scaleY(0.7) transform:scaleY(0.7) &:after -webkit-transform: scaleY(0.7) transform:scaleY(0.7) @media(-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2)//DPR为2的缩放0.5倍 .border-1px &:before -webkit-transform: scaleY(0.5) transform:scaleY(0.5) &:after -webkit-transform: scaleY(0.5) transform:scaleY(0.5)- 定义index.styl: 引用所有styl文件,最后在 main.js 中全局引用
@import"./mixin" //import后无空格 @import"./icon" @import"./base"main.js: import '@/common/stylus/index.styl' //import后有空格
六、header组件开发 vue-resource
- 安装 vue-resource
1
npm
install
vue-resource --save
注意:每次install完插件等之后需要重新启动项目
- main.js 文件中:
import VueResource from 'vue-resource' Vue.use(VueResource)之后就可以在项目任何地方:使用 this.$http 命令
- App.vue 组件中:
- export module{} 外:
const ERR_OK = 0; //定义常量,增强程序可读性- export module{} 内:
data() { return { seller:{} //维护数据 seller } }
- 异步请求数据,返回的是Promise对象
created: function () { this.$http.get('/api/seller') //发送get请求, .then(function(res){ //.then方法 请求完成后调用 //第一个函数是请求成功后方法 }, function (err) { //第二个函数是请求失败后方法 }) }使用ES6 箭头函数:箭头函数前后必须有空格
created: function () { this.$http.get('/api/seller') .then((res) => { res = res.body //拿到response返回的promise对象的body(Data Object) if (res.errno === ERR_OK) { this.seller = res.data; //console.log(this.seller) } }, (err) => { }) }外部组件
- 父组件 App.vue 中
组件标签中用v-bind绑定seller属性,传给子组件seller数据
- 子组件 header.vue 中通过 props属性 获取父组件传来的seller数据
props: { seller: { type: Object } }- 模板对应位置 显示 对应seller.xxx 子数据
- 标签: 使用seller数据图片地址,v-bind绑定src属性
:src="seller.avatar"- 文本内容: 双向数据绑定显示seller数据
{{seller.name}}- 如果要获取的是 seller数据对象的 子对象数组的 某一项:因为异步获取数据,子对象可能为undefined,需要先 v-if 判断是否存在
{{seller.supports[0].description}}- 定义 classMap数组,通过获取seller数据中的索引值,应用对应索引的class
created (){ this.classMap = ['decrease','descount','guarantee','invoice','special'] }- mixin.styl 文件中伪函数:实现图片在不同DPR下引用不同的图片路径
bg-image($url) background-image: url($url+"@2x.png") @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3) background-image: url($url+"@3x.png")
- 公告内容 —— 文字【省略号效果】
white-space: nowrap overflow: hidden text-overflow: ellipsis- 背景图片【模糊滤镜效果】
.background position: absolute top: 0 left: 0 width: 100% height: 100% z-index: -1 filter: blur(10px)详情弹层页
- 实现弹出层
- v-show指令 —— 控制弹出层的显示/隐藏
data () { return { detailShow: false //通过改变数据detailShow 的true/false,控制元素的显示/隐藏 } }- @click —— 触发点击事件,执行显示函数
methods: { showDetail () { this.detailShow = true; } }
【Css Sticky footers布局】
- Sticky footers设计:
- 如果页面内容不够长的时候,页面块粘贴在视窗底部;
- 如果内容足够长时,页面块会被内容向下推送(区别于fixed)
- 相对复杂但兼容性最好的一个方案:
- 套路布局
1
2
3
4
5
6
7
8
<
div
class="detail" v-show="detailShow">
<
div
class="detail-wrapper clearfix"> //外层wrapper,min-height: 100%
<
div
class="detail-main">
div
> //内容层 padding-bottom: 64px
div
>
<
div
class="detail-close">
<
i
class="icon-close">
i
> //要适应内容显示的关闭按钮 margin-top: -64px
div
>
div
>
关键:padding-bottom撑开一个高度,为关闭按钮留下位置
1
2
3
.detail-main
margin-top
:
64px
padding-bottom
:
64px
样式:
+ View Code
Star组件抽象
- 目标:为了增强扩展性,使足够灵活
- 思路:
- v-for —— 根据分数 遍历itemClasses 显示星星样式
- props —— 从父组件接收两个参数:size尺寸,score分数
props:{ size: { type: Number }, score: { type: Number } }- :class —— 绑定动态class, 在不同的调用地方, 可以设置不同的样式
View Code
- computed —— 根据size 计算出动态的class;根据score push对应个数的全亮星星class;判断如果有半分或不足5分的,push进半星class和灰色星class;根据数组中对应的class显示对应的星星图片
const LENGTH = 5; const CLS_ON = 'on'; const CLS_HALF = 'half'; const CLS_OFF = 'off'; computed: { starType() { return 'star-' + this.size; //根据size 计算出动态的class }, itemClasses() { let result = []; let score = Math.floor(this.score*2)/2; let hasDecimal = score % 1 !== 0; let integar = Math.floor(score); for(let i=0; i
- 样式: 除了通用样式,还有根据不同size计算出的全部class的样式
小标题自适应线
- 避免:写死百分比,这样宽屏幕会间隔很大,窄屏幕间隔会几乎看不到
- flex布局:
优惠信息.title display: flex width: 80% margin: 30px auto 24px auto .line flex: 1 position: relative top: -6px border-bottom: 1px solid rgba(255, 255, 255, 0.2) .text padding: 0 12px font-size: 14px
【Postcss工具】
- vue-loader 编译vue文件时,会使用Postcss工具,自动为有兼容性问题的css属性加上浏览器前缀
- Postcss是根据can i use官网去写代码,基本不会出现兼容性问题
- Can I Use 是一个检测浏览器对JS、HTML5、CSS、SVG或者其他Web前端相关特性支持程度的列表
- 可以检测的浏览器包括桌面和移动版的主流浏览器:IE, Firefox, Chrome, Safari和 Opera等
过渡动画组件 transition
.detail opacity: 1 background: rgba(7, 17, 27, 0.8) &.fade-enter-active, &.fade-leave-active transition: all 0.5s ease &.fade-enter, &.fade-leave-active opacity: 0 background: rgba(7, 17, 27, 0)iPhone手机背景模糊效果
backdrop-filter: blur(10px) // PC端和其它手机看不出效果