仿魅族商城项目实战开发

1.准备工作

1.1.环境搭建

1.2.mock 数据和项目需求

项目需求

参考完成的线上 demo: 仿魅族商城实战

静态资源

静态资源下载地址:点击下载

svg 生成图标字体

使用 iconfont.cn 这个网站生成图标字体

mock 数据

创建一个项目,然后在这个项目里面安装 json-server

mkdir meizu-api
cd meizu-api
npm init -y
npm i json-server

在项目下新建 server.js 文件,文件内容如下:

const JsonServer = require("json-server")
const data = require("./db.js")

const server = JsonServer.create()
const router = JsonServer.router(data)
const middleware = JsonServer.defaults()

server.use(middleware)
server.use(router)
server.use(JsonServer.bodyParser)

server.listen(
  {
    host: "127.0.0.1",
    port: 8848
  },
  () => {
    console.log("json-server is running on port 8848")
  }
)

在项目下新建 db.js 文件,文件内容可以去静态资源包里面找

接下来,启动 json-server 服务器

node server.js

然后在浏览器中访问

http://127.0.0.1:8848

统一代码风格

安装 Prettier - Code formatter 扩展,在项目目录下新增.prettirerc 配置文件,输入以下内容

{
  "eslintIntegration": true,

  "singleQuote": true,

  "semi": false
}

修改.esintrc.js 文件

module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: ["plugin:vue/essential", "@vue/prettier"],
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
    //强制使用单引号
    quotes: ["error", "single"],
    //强制不使用分号结尾
    semi: ["error", "never"]
  },
  parserOptions: {
    parser: "babel-eslint"
  }
}

接下来去 vscode 中设置保存自动格式化

使用 git 管理项目

初始化项目

git init

2.Header 组件和悬浮列表动画

导入项目静态资源跑通项目

.
+-- public
+-- src
|   +-- assets
|       +-- css   // 把reset.css放到这
|       +-- images  // 把静态资源里面的图片放到这里
|       +-- fonts   // 把在iconfont网站生成的font字体放到这里
|   +-- components
|   +-- views
|       +-- Index.vue
|   +-- App.vue
|   +-- main.js
|   +-- router.js
|   +-- store.js

router.js 中代码

import Vue from "vue"
import Router from "vue-router"
import Index from "./views/Index.vue"

Vue.use(Router)

export default new Router({
  mode: "history",
  base: process.env.BASE_URL,
  routes: [
    {
      path: "/",
      name: "index",
      component: Index
    }
  ]
})

App.vue 中代码

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">首页|</router-link>
    </div>
    <router-view />
  </div>
</template>

<style></style>

App.vue 中的代码

<template>
  <div class="home">
    <h1>这里是首页</h1>
  </div>
</template>

<script>
// @ is an alias to /src

export default {};
</script>

进入项目目录,运行查看效果

npm run serve

在浏览器输入: http://localhost:8080 查看效果,到此为止,我们的项目准备工作已经完成,并且项目已经跑通,接下来,我们就按部就班开发相关组件就行

主页整体布局框架搭建

Index.vue 中的代码

<template>
  <div>
    <!-- 头部区域 -->
    <div class="header">头部</div>
    <!-- 轮播图 -->
    <div class="swiper-wraper">轮播图</div>
    <!-- 广告图 -->
    <ul class="post-wraper">
      <li>广告</li>
    </ul>
    <!-- 展示区 -->
    <div class="index-container">展示区</div>
    <!-- 底部 -->
    <div class="footer">底部</div>
  </div>
</template>

<script>
// @ is an alias to /src
export default {}
</script>


接下来需要在 main.js 中引入样式重置的 css 文件以及图标字体样式

import "./assets/css/reset.css"
import "./assets/fonts/iconfont.css"

Header 组件开发

在components里面新建一个Header组件

<template>
  <div class="header">
    <div class="header-wraper clearfix">
      <div class="header-logo pull-left"></div>
      <div class="header-cart pull-right">
        <span class="icon-meizu icon-cart"></span>
      </div>
      <div class="header-user pull-right">
        <span class="icon-meizu icon-user"></span>
      </div>
      <div class="header-search pull-right">
        <input type="text" class="search-input" placeholder="购物车" />
        <span class="icon-input icon-meizu icon-search"></span>
      </div>
      <ul class="header-nav pull-right">
        <li
          v-for="(navitem, index) in navData"
          :key="index"
          @mouseenter="showChildren(navitem.children)"
        >
          <a class="nav-item" href="">{{ navitem.name }}</a>
        </li>
      </ul>
      <transition name="nav">
        <div
          class="nav-children"
          v-show="isShowChildren"
          @mouseleave="hideChildren"
        >
          <div class="children-wrapper">
            <transition-group tag="ul" @enter="enter">
              <li
                class="children-item"
                v-for="(childrenitem, index) in navChildren"
                :key="childrenitem.pic"
                :data-index="index"
              >
                <img :src="childrenitem.pic" alt="" />
                <p>{{ childrenitem.name }}</p>
                <p>{{ childrenitem.price }}</p>
              </li>
            </transition-group>
          </div>
        </div>
      </transition>
    </div>
  </div>
</template>

<script>
import Velocity from 'velocity-animate'
export default {
  data() {
    return {
      navData: [],
      navChildren: [],
      isShowChildren: false
    }
  },
  mounted() {
    this.getNavData()
  },
  methods: {
    async getNavData() {
      const { data } = await this.axios.get('/api/nav')
      this.navData = data
      console.log(data)
    },
    showChildren(data) {
      this.isShowChildren = true
      this.navChildren = data
    },
    hideChildren() {
      this.isShowChildren = false
    },
    enter(el) {
      console.log(el)
      const timeout = el.dataset.index * 150
      setTimeout(() => {
        Velocity(el, {
          opacity: 1,
          translateX: '-50px'
        })
      }, timeout)
    }
  }
}
</script>

<style scoped lang="less">
.header {
  width: 100%;
  background-color: white;
  position: relative;
}
.header-wraper {
  width: 1240px;
  margin: 0 auto;
  .header-logo {
    width: 140px;
    height: 26px;
    margin: 28px 0;
    background: url('../assets/images/logo.png') no-repeat center;
  }
  .header-cart span,
  .header-user span {
    font-size: 24px;
  }
  .header-cart,
  .header-user {
    line-height: 1;
    padding: 28px 20px 0 20px;
  }
  .header-search {
    border: 1px solid #ddd;
    border-radius: 2px;
    height: 32px;
    width: 172px;
    position: relative;
    margin-top: 25px;
    .search-input {
      width: 127px;
      height: 20px;
      border: none;
      position: absolute;
      left: 5px;
      top: 6px;
      font-size: 12px;
    }
    .icon-input {
      font-size: 16px;
      position: absolute;
      right: 10px;
      top: 4px;
    }
  }
}
.header-nav {
  li,
  a {
    display: inline-block;
    font-size: 16px;
  }
  a.nav-item {
    padding: 32px 20px;
    line-height: 1;
    cursor: pointer;
    transition: color 1s;
    &:hover {
      color: #31a5e7;
    }
  }
}
.nav-children {
  width: 100%;
  height: 156px;
  background-color: white;
  position: absolute;
  left: 0;
  top: 82px;
  border: 1px solid #e9e9e9;
  z-index: 1;
  overflow: hidden;
  .children-wrapper {
    width: 1240px;
    margin: 0 auto;
    padding-left: 180px;
    .children-item {
      width: 136px;
      height: 145px;
      display: inline-block;
      text-align: center;
      font-size: 12px;
      color: #666;
      opacity: 0;

      img {
        width: 100px;
        height: 100px;
      }
    }
  }
}

.nav-active {
  transition: height 3s ease-in-out;
}
.nav-enter {
  height: 0;
}
.nav-enter-to {
  height: 156px;
}
</style>

在Index.vue组件中引入Header组件

<template>
  <div>
    <!-- 头部区域 -->
    <Header />
    <!-- 轮播图 -->
    <div class="swiper-wraper">轮播图</div>
    <!-- 广告图 -->
    <ul class="post-wraper">
      <li>广告</li>
    </ul>
    <!-- 展示区 -->
    <div class="index-container">展示区</div>
    <!-- 底部 -->
    <div class="footer">底部</div>
  </div>
</template>

<script>
// @ is an alias to /src
import Header from '@/components/Header'
export default {
  components: {
    Header
  }
}
</script>

3.轮播组件

4.商品列表

5.广告商品

7.分类组件

8.图片展示组件

9.条件筛选组件

10.排序组件

11.推荐组件

12.详情页

13.购物车

14.订单