• 【饿了么】—— Vue2.0高仿饿了么核心模块&移动端Web App项目爬坑(一)


    前言:学习Vue.js高仿饿了么课程过程中,总结了这个Web App项目从准备到开发完毕自己觉得很重要的知识点。这一篇主要介绍:项目准备、页面骨架开发、header组件开发。

    项目github地址:https://github.com/66Web/ljq_eleme,欢迎Star。


    App header
    一、项目分析&学习目标

           当前最火的MVVM框架

    • Vue.js —— 轻量、简洁、高效、数据驱动、组件化

          高仿上线外卖App标准来开发

    • 核心模块 —— 商家模块

          开发一个webApp的全流程

    1. 需求分析
    2. 脚手架工具
    3. 数据mock
    4. 架构设计
    5. 代码编写
    6. 自测
    7. 编译打包

          以线上生产环境的代码质量作标准

    • 代码开发及测试环节:
    1. UI标注
    2. 真实数据演示
    • 代码规范
    1. 架构设计
    2. 组件抽象
    3. 模块拆分
    4. 代码风格统一
    5. JS变量命名规范
    6. CSS代码规范

          功能技术分析

    • vue-resource:  和后端做数据交互
    • vue-router:  做前端路由,实现单页应用
    • 第三方JS库better-scroll:  列表滚动的实现
    • 最大程度组件化: 提高代码的复用
    • html5的localstorage【收藏商家】功能—存储在浏览器端
    • 细节:图标字体的使用
    • 移动端1像素边框
    • css sticky footer布局
    • flex 弹性布局

          学习目标

    • 掌握Vue.js在实战中的运用
    • 学会使用Vue.js【完整的】开发移动端App
    • 学会组件化、模块化的开发

          学习内容

    • Vue.js框架介绍
    • Vue-cli 脚手架 —— 搭建基本代码框架
    • vue-router 官方插件 —— 管理路由
    • vue-resource 官方插件 —— 和后端作Ajax通信
    • Webpack 开源构建工具(把源代码经过编译生成浏览器可以识别和运行的代码)
    • es6 + eslint eslint —— es6代码风格检查工具
    • 工程化 组件化 模块化
    • 移动端常用开发技巧:
    1. flex弹性布局
    2. css stickyfooter
    3. 酷炫的交互设计
    二、Vue.js介绍

          近年来前端开发趋势

    • 旧浏览器逐渐淘汰,移动端需求增加
    • 前端交互越来越多,功能越来越复杂
    • 架构从传统后台MVC 向REST API+ 前端MV* 迁移

          (前者传统MVC:更新数据会刷新页面 后者前端MV*: 向后端REST API异步请求数据,局部刷新页面)

            MV* —— MVC、MVP、MVVM

          MVVM框架

          View    ViewModel    Model

           视图        通讯           数据

    • DOM 观察者 Javascript对象
    1. 针对具有复杂交互逻辑的前端应用
    2. 提供基础的架构抽象
    3. 通过Ajax数据持久化,保证前端用户体验
    • MVVM框架技术:vue.js、react.js、Angular.js

          对比Anglar  React

    • Vue.js更轻量,gzip后大小只有 20K+
    • Vuejs更易上手,学习曲线平稳
    • 吸收两家之长,借鉴了angular的指令和react的组件化

          vue.js 核心思想

    • 数据驱动
    • 组件化

          组件设计原则

    • 页面上每个独立的可视/可交互区域视为一个组件
    • 每个组件对应一个工程目录,组件所需要的各种资源在这个目录下【就近维护
    • 页面不过是组件的容器,组件可以嵌套自由组合形成完整的页面
    三、Vue-cli开启Vue项目

          Vue-cli 是Vue的脚手架工具 —— 帮助写好Vue基础代码的工具

    1. 目录结构
    2. 本地调试
    3. 代码部署
    4. 热加载
    5. 单元测试

          安装使用

    (sudo) npm install -g vue-cli  // sudo:mac环境下有关管理权限的命令
    
    vue init webpack my-project
    

          项目文件

    • src文件夹:存放项目源码
    • bulld目录+ config目录:webpack配置相关
    • node_modules文件夹:npm install 安装的依赖代码库
    1. static—>.gitkeep: 当这个目录为空时也可以将它提交到git仓库中
    2. babelrc : babel的一些配置,es6语法的转换
    3. .editorconfig: 编辑器的配置
    4. .eslintignore: 忽略语法检查的目录文件,一般忽略build目录和node_modules目录
    5. .eslintrc.js: eslint的配置文件
    6. gitignore: 上传git仓库要忽略的一些文件的配置
    7. index.html: 入口html文件,要使用的css和js文件会在编译过程中自动插入
    8. package.json:整个项目的配置文件,一般用来描述项目 ↓

            →  scripts:  配置一些需要执行的命令
            →  dependencies:开发环境中的依赖
            →  devdependencies: 编译过程中的依赖

          项目运行

    npm run dev 
    • src开发目录下:
    1. main.js —— 项目入口文件
    2. App.vue —— 主页面组件
    • vue语法糖:  export default { } 一个对象——可以定义一个组件
    【小知识点】sublime自动格式化 —— Command+option+L 或 Control+alt+L
    • 在父组件中使用子组件,如Hello.vue:
    1. 引用 
      import Hello from './compoments/Hello'
    2. 注册
      export default{
            components: {
                 Hello   //es6语法  相当于 'Hello': Hello
            }
      }
    3. 使用标签
      <hello><hello>

          开发时的Webpack配置与编译

    • build->dev-server.js 或 Webpack.dev.conf.js
    • webpack.base.conf.js : 配置各种文件的Loader

          →  配置默认识别的路径

    resolve: {
         extensions: ['.js', '.vue', '.json'],
          alias: {
               'vue$': 'vue/dist/vue.esm.js',
               '@': resolve('src'),
           }
    }
    四、准备工作

          图标字体制作 

    • 在线制作网站:https://icomoon.io/app/#/select
    • 将自己的SVG图标导入,输出自己的图标字体文件
    • Import Icons → Generate Fonts → preferences修改名称 → Download
    • 使用:icon.css和fonts文件夹下所有文件

          项目目录设计

    • src->common目录下:项目公用文件 js、style、fonts

    【css的stylus语法】

    • 删掉分号和大括号, &表示父元素,冒号也可以省略
    • 文件后缀为styl,style中添加lang="stylus"
    • 需要安装:
    npm install stylus stylus-loader --save-dev



    • resource目录下:项目图片文件——可以删掉无用的assets目录,但需要修改引用到的地方
    • components目录下:布局、业务功能等分模块管理组件,如header目录,footer目录
    • static->css目录下:reset.css 标签默认样式
    • 在  index.html 中引入:
    <link rel="stylesheet" type="text/css" href="static/css/reset.css">

          前后端分离

    • Vue SPA —— 前端通过 vue-resource  Ajax从后端获取数据
    • 前端最重要的任务:mock数据(后台数据模拟) data.json
    {
        "seller":{} //商家相关字段
        "goods":{} //商品相关字段
        "rattings":{} //评论相关字段
    } 

          webpack.dev.conf.js中配置

    • 使用 express框架  开启一个node server,用 express.Router 编写这些接口请求
    1. 首先:在 const portfinder = require(‘portfinder’) 后添加
      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对象
    2. 然后:找到 devserver{}, 在里面添加
      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
                  })
           })
      }
    • 注意:每次配置完 express 之后都需要重新启动

          查看json数据

    • 在Google地址栏中输入:localhost:8080/api/seller 
    • 依赖Google的jsonview插件 —— 安装 使数据格式化

    【Google安装第三方插件】

    • 打开https://github.com ;
    • 搜索 jsonView 链接:https://github.com/search?utf8=%E2%9C%93&q=jsonview;
    • 选择需要的插件(我下载的是这个gildas-lormeau/JSONView-for-Chrome); 
    • 点击【Download Zip】,插件下载完成,解压缩到相应目录(D:DownloadJSONView-for-Chrome-master); 
    • 安装,打开chrome - 扩展程序 (地址栏输入chrome://extensions/); 
    • 右上角,选中开发模式; 
    • 点击”加载正在开发的扩展程序…” 
    • 选择插件目录(D:DownloadJSONView-for-Chrome-masterWebContent); 
    • 安装完成,重新加载 (Ctrl+R)。 
    • 测试地址:http://jsonview.com/example.json

    —— 转载自【小白白打酱油博客

    五、页面骨架开发

          移动端视口

    • index.html   中通过meta设置视口可被缩放,初试宽高设置
    <meta name="viewport" 
    content="width=device-width,initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">

          App.vue 中把页面拆为三个区块

    <div id="app">
         <div class="header">header</div>
         <div class="tab">tab</div>
         <div class="content">content</div>
    </div>

        然后,分别抽成一个组件,引用 —— 所有组件自定义标签名不可与html本身标签重合 'v-header': header 

          移动端经典布局  flex 

    <div class="tab">
          <div class="tab-item">商品</div>
           <div class="tab-item">评论</div>
           <div class="tab-item">商家</div>
    </div>
    .tab
       display: flex
        100%
       height: 40px
       line-height: 40px
       .tab-item
             flex:1
             text-align: center

          vueRouter

    1. Vue2.0 使用 <router-link> 进行【导航】
      <router-link :to="{ path: '/goods' }">商品</router-link>
    2. 【路由外链 】<router-view> —— 单页面切换的内容页,替换content div区块
      <router-view></router-view>
    3.  main.js 中设置单页面应用路由的【挂载组件】—— 默认App.vue 也可以自定义组件如layout.vue

      /* eslint-disable no-new */
      new Vue({
          el: '#app',
          router,
          components: { App },
          template: '<App/>' 
      })
    4. 配置【路由map】:router->index.js

      export default new Router({
             mode: 'history',
             routes: [
                            {
                               path: '/',
                               redirect: '/goods',//默认页面重定向
                             },
                            {
                               path: '/goods',
                               component: goods
                             },
                            {
                               path: '/ratings',
                               component: ratings
                            },
                         ]
      })
    5. 【点击】高亮显示   a.router-link-active 
      #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加1像素边框  X
      .tab{
           border-bottom: 1px solid rgba(7,17,27,0.1)
      }
    • 问题是:这段代码在PC端显示,是1像素,但是在手机端显示,就不是1像素。
    • 因为手机端有一个DPR的概念:它的物理像素是设备像素的两倍。所以iPhone6上面可能就是一个2像素的边框

    【PC开发中用手机实时预览的小技巧】

    • 新开一个gitbash,mac环境下输入命令:ifconfig, windows下输入:ipconfig
    • 获知本机IP地址192.168.1.1,替换掉localhost(表示本机)
    • 将地址http://192.168.1.1:8080/goods#/输入到草料二维码网站中,生成二维码,用手机扫描,即可查看
    • 必须与PC使用的是同一局域网
    • 正确做法:给 tab 加一个伪类:after , 让它是一条1像素的线,然后在DBR为2或3的手机端缩放 
      <div class="tab border-1px">
    1. 定义mixin.styl:  通过css预处理器的函数方法,实现伪类线
      border-1px($color)
            position: relative
            &:before
                display: block
                position: absolute
                left:0
                top: 0
                 100%
                border-bottom: 1px solid $color
                content: ''
            &:after
                display: block
                position: absolute
                left:0
                bottom: 0
                 100%
                border-top: 1px solid $color
                content: ''
    2. 定义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)
    3. 定义index.styl: 引用所有styl文件,最后在 main.js 中全局引用
      @import"./mixin"   //import后无空格
      @import"./icon"
      @import"./base"
      main.js: import '@/common/stylus/index.styl'   //import后有空格
    六、header组件开发

          vue-resource

    1. 安装 vue-resource
      npm install vue-resource --save
      

      注意:每次install完插件等之后需要重新启动项目

    2. main.js 文件中:
      import VueResource from 'vue-resource'
      Vue.use(VueResource)

      之后就可以在项目任何地方:使用 this.$http 命令

    3. 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 中 <header> 组件标签中用v-bind绑定seller属性,传给子组件seller数据
      <v-header :seller="seller"></v-header>
    • 子组件 header.vue 中通过 props属性 获取父组件传来的seller数据
      props: {
         seller: {
             type: Object
         }
      }
    • 模板对应位置 显示 对应seller.xxx 子数据
    1. <img>标签: 使用seller数据图片地址,v-bind绑定src属性
       :src="seller.avatar"
    2. 文本内容: 双向数据绑定显示seller数据
      {{seller.name}}
    3. 如果要获取的是 seller数据对象的  子对象数组的  某一项因为异步获取数据,子对象可能为undefined,需要先 v-if 判断是否存在
      <div class="support" v-if="seller.supports">
            <span class="icon" :class="this.classMap[seller.supports[0].type]"></span>
            <span class="text">{{seller.supports[0].description}}</span>
      </div>
    4. 定义 classMap数组,通过获取seller数据中的索引值,应用对应索引的class
      created (){
           this.classMap = ['decrease','descount','guarantee','invoice','special']
      }
      <span class="icon" :class="this.classMap[seller.supports[0].type]"></span>
    5. 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
           100%
          height: 100%
          z-index: -1
          filter: blur(10px)   

          详情弹层页

    • 实现弹出层
    1. v-show指令 ——  控制弹出层的显示/隐藏
      <div class="detail" v-show="detailShow"></div>
      data () {
           return {
                detailShow: false  //通过改变数据detailShow 的true/false,控制元素的显示/隐藏
           }
      }
    2. @click —— 触发点击事件,执行显示函数
      <div class="bulletin-wrapper" @click="showDetail>
      methods: {
          showDetail () {
               this.detailShow = true;
          }
      }

    【Css Sticky footers布局】

    • Sticky footers设计:
    1. 如果页面内容不够长的时候,页面块粘贴在视窗底部;
    2. 如果内容足够长时,页面块会被内容向下推送(区别于fixed)
    • 相对复杂但兼容性最好的一个方案:
    1. 套路布局
      <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>
    2. 关键:padding-bottom撑开一个高度,为关闭按钮留下位置

      .detail-main
           margin-top: 64px
           padding-bottom: 64px  
    3. 样式:

      .detail-wrapper
          min-height: 100%
          .detail-main
               margin-top: 64px
               padding-bottom: 64px
          .detail-close 
               position: relative
                32px
               height: 32px
               margin: -64px auto 0 auto 
               clear: both
               font-size: 14px  

           Star组件抽象 

    • 目标:为了增强扩展性,使足够灵活
    • 思路:
    1. v-for ——  根据分数 遍历itemClasses  显示星星样式
      <div class="star" :class="starType">
               <span v-for="itemClass in itemClasses" 
                     :key="itemClass.value" 
                     :class="itemClass" 
                     class="star-item">
               </span>
      </div>
    2. props —— 从父组件接收两个参数:size尺寸score分数
       props:{
             size: {
                 type: Number
             },
             score: {
                 type: Number
             }
      }
    3. :class  —— 绑定动态class,  在不同的调用地方, 可以设置不同的样式
       @import "../../common/stylus/mixin"
      
          .star
             .star-item
                 display: inline-block
                 background-repeat: no-repeat
             &.star-48
                 .star-item
                      20px
                     height: 20px
                     margin-right: 22px
                     background-size: 20px 20px
                     &:last-child
                         margin-right: 0
                     &.on
                         bg-image('star48_on')
                     &.half
                         bg-image('star48_half')
                     &.off
                         bg-image('star48_off')    
             &.star-36
                 .star-item
                      15px
                     height: 15px
                     margin-right: 16px
                     background-size: 15px 15px
                     &:last-child
                         margin-right: 0
                     &.on
                         bg-image('star36_on')
                     &.half
                         bg-image('star36_half')
                     &.off
                         bg-image('star36_off') 
             &.star-24 
                 .star-item
                      10px
                     height: 10px
                     margin-right: 3px
                     background-size: 10px 10px
                     &:last-child
                         margin-right: 0
                     &.on
                         bg-image('star24_on')
                     &.half
                         bg-image('star24_half')
                     &.off
                         bg-image('star24_off')      
      View Code
    4. 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<integar; i++){
                      result.push(CLS_ON)  //根据score 在itemClasses中push进对应个数的全亮星星class
                  }
                  if(hasDecimal) {
                      result.push(CLS_HALF);//判断如果有半分或不足5分的,push进半星class和灰色星class
                  }
                  while (result.length < LENGTH) {
                      result.push(CLS_OFF)
                  }
      
                  return result; //根据itemClasses中对应的class显示对应的星星图片
              }
          }
    • 样式: 除了通用样式,还有根据不同size计算出的全部class的样式

           小标题自适应线

    • 避免:写死百分比,这样宽屏幕会间隔很大,窄屏幕间隔会几乎看不到
    • flex布局
    <div class="title">
           <div class="line"></div>
           <div class="text">优惠信息</div>
           <div class="line"></div>
    </div>
    .title
        display: flex
         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

    <transition name="fade">
            <div class="detail">
    </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端和其它手机看不出效果

    注:项目来自慕课网

  • 相关阅读:
    数据库
    知道版本对于出0day后批量攻击dedecms有非常大的帮助,先判断版本再选择相应exp,效率大增
    跟我开发NSP(网上查询平台):如何选择开发项目
    Python3基础教程(十七)—— Virtualenv
    Python3基础教程(十六)—— 迭代器、生成器、装饰器
    Python3基础教程(十五)—— PEP8 代码风格指南
    Python3简明教程(十四)—— Collections模块
    Python3简明教程(十二)—— 模块
    Python3简明教程(十一)—— 类
    Python3简明教程(十)—— 异常
  • 原文地址:https://www.cnblogs.com/ljq66/p/9980372.html
Copyright © 2020-2023  润新知