vue_eHungry 饿了么
eHungry 仿饿了么
- git 操作
git checkout -b dev // 创建新分支 dev
git push origin dev // 代码推送到 dev
------------------------------- 代码合并到主分支 ------------------------------
git checkout master
git merge dev
- 安装 stylus 依赖包
npm install stylus stylus-loader --save-dev
common/stylus/mixins.styl -------- 复用样式代码
-
$green = #02a774; $yellow = #F5A100; $bc = #e4e4e4; // 一像素下边框 bottom-border-1px($color) position relative border none &:after content '' position absolute left 0 bottom 0 width 100% height 1px background-color $color transform scaleY(0.5) // 一像素上边框 top-border-1px($color) position relative &::before content '' position absolute z-index 200 left 0 top 0 width 100% height 1px background-color $color //根据像素比缩放1px像素边框 @media only screen and (-webkit-device-pixel-ratio: 2 ) .border-1px &::before transform scaleY(.5) @media only screen and (-webkit-device-pixel-ratio: 3 ) .border-1px &::before transform scaleY(.333333) //根据像素比来使用 2x图 3x图 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") //清除浮动 clearFix() *zoom 1 &::after content '' display block clear both
- 入口文件配置 main.js
-
import Vue from "vue"
import App from "./App.vue" new Vue({ el: "#app", /* components: {App}, template: "<App/>" */ /* 或者 render: function (createElement){createElement(App)} */ render: h=>h(App) }) - 路由器 router 配置 ---- 记得在 main.js 中加入 router
npm install vue-router --save
-
import Vue from "vue" import App from "./App.vue" import Home from "../pages/Home" import Search from "../pages/Search" import Order from "../pages/Order" import Personal from "../pages/Personal" Vue.use(VueRouter) new VueRouter({ mode: "history", // 去掉 # routes: [ {path: "/home", component: Home}, {path: "/search", component: Search}, {path: "/order", component: Order}, {path: "/personal", component: Personal}, {path: "/", redirect: "/home"} ] })
- 代理 /config/index.js ---- 改完配置要重启
dev:{
proxyTable: { // 更强大的代理
"api" :{ // 代理 /api 开头的请求
target: "http://localhost:5000", // 代理目标的基础路径
changeOrigin: true, // 支持跨域
pathRewrite: { // 重写路径: 去掉开头的 /api
"^/api": ""
}
},
"reqBaidu" :{ // 代理 /baidu 开头的请求
target: "http://localhost:5000", // 代理目标的基础路径
changeOrigin: true, // 支持跨域
pathRewrite: { // 重写路径: 去掉开头的 /baidu
"^/baidu": ""
}
}
}
}
- /src/api/ajax.js
import axios from "ajax"
export default function ajax(url, data={}, method="GET"){
return new Promise((resolve, reject)=>{
let promise = null
if(method==="GET"){
// params 指的是 query 请求字符串
promise = axios.get(url, {params: data})
}else if(method==="POST"){
promise = axios.post(url, data)
}
// promise.then(response=>resolve(response.data)).catch(err=>reject(err))
promise.then(response=>resolve(response.data)).catch(err=>alert(err)) // 外部无需多个 try catch 处理 reject
})
}
- /src/api/request.js -------- 参考接口文档,请求后台数据
import ajax from "./ajax"
// BASE = "http://localhost:5000"
const BASE = "/api" // proxTable 代理 /api 开头的请求
export function requestAddress(longitude, latitude){
// 根据 纬度,经度 获取店铺位置
return ajax(`/position/${longitude},${latitude}`);
}
export const requestCategory = ()=>ajax("/index_category"); // 获取商品分类
export const requestShops = (longitude, latitude)=>ajax("/shops", {latitude, longitude}); // 获取店
- Vuex.Store
npm install vuex --save
/src/vuex/store/index.js
-
import Vue from "vue"; import Vuex from "vuex"; import state from "./state" import mutations from "./mutations" import actions from "./actions" import getters from "./getters" Vue.use(Vuex); export default new Vuex.Store({ state, mutations, actions, getters })
/src/vuex/store/state.js
-
export default { isLoginRegister: false, longitude: "116.36867", latitude: "40.10038", position: {}, categorysArr: [], shops: [] }
/src/vuex/store/actions.js
-
import { TOLOGINREGISTER, SAVE_POSITION, SAVE_CATEGORY, SAVE_SHOPS } from "./mutation-type" import {requestPosition, requestCategory, requestShops} from "../../api" export default { toLoginRegister({commit}, to){ commit(TOLOGINREGISTER, to); }, async getPosition({commit, state}){ const {longitude, latitude} = state const result = await requestPosition(longitude, latitude) if(result.code === 0){ commit(SAVE_POSITION, result.data) } }, async getCategorys({commit}){ const result = await requestCategory() if(result.code === 0){ commit(SAVE_CATEGORY, result.data) } }, async getShops({commit, state}){ const {longitude, latitude} = state const result = await requestShops(longitude, latitude) if(result.code === 0){ commit(SAVE_SHOPS, result.data) } } }
/src/vuex/store/mutations.js
-
import { TOLOGINREGISTER, SAVE_POSITION, SAVE_CATEGORY, SAVE_SHOPS } from "./mutation-type" export default { [TOLOGINREGISTER] (state, to) { state.isLoginRegister = to }, [SAVE_POSITION] (state, position) { state.position = position }, [SAVE_CATEGORY] (state, categorysArr) { state.categorysArr = categorysArr }, [SAVE_SHOPS] (state, shops) { state.shops = shops } }
/src/vuex/store/mutations-type.js
-
export const TOLOGINREGISTER = "toLoginRegister" export const SAVE_POSITION = "save_position" export const SAVE_CATEGORY = "save_category" export const SAVE_SHOPS = "save_shops"
/src/vuex/store/getter.js
- 路由元信息 - 状态数据共享
/src/router/router.js
-
import Vue from "vue"; import VueRouter from "vue-router"; import Home from "../pages/Home/Home" import Search from "../pages/Search/Search" import Order from "../pages/Order/Order" import Personal from "../pages/Personal/Personal" import LoginRegister from "../pages/LoginRegister/LoginRegister" Vue.use(VueRouter); export default new VueRouter({ mode: "history", routes: [ {path:"/home", component: Home, meta:{showFooter: true}}, {path:"/search", component: Search, meta:{showFooter: true}}, {path:"/order", component: Order, meta:{showFooter: true}}, {path:"/personal", component: Personal, meta:{showFooter: true}}, {path:"/login_register", component: LoginRegister}, {path:"/", redirect: "/home"}, ] })
- /src/App.vue
-
<template> <div> <router-view></router-view> <FooterNav v-show="$route.meta.showFooter"></FooterNav> </div> </template> ...
-
watch: { categorysArr () { // 在下次 DOM 更新后,再执行回调 this.$nextTick(()=>{ const mySwiper = new Swiper('.swiper-container', { loop: true, // 循环模式选项 pagination: { // 如果需要分页器 el: '.swiper-pagination', }, }) }) } },
图片验证码点击,重新请求
改变 img 的 src,每次都必须赋新的值
this.$refs.captcha.src = "http://localhost:5000/captcha?curTime="+Data.now
Account SID
Auth Token
Rest URL 都一样
测试号码必须填
SMS 普通短信 sms_utilt.js 配置
MMS 彩信
-----------------------------------------------------------------------------------------
cookie 分两类
会话 cookie - 保存在浏览器运行时内存中,关闭浏览器后,数据清除 - 会话的标识
持久化 cookie - 保存在浏览器管理的文件中,关闭浏览器后,数据还在
session 会话 - 保存在服务器端,用于存储数据的容器 ---------- session 依赖于 cookie,浏览器可以禁用 cookie
会话: 浏览器的打开到关闭,整个过程是一个会话
session 对象保存在服务器存储数据的容器
一旦产生这个对象,服务器会自动向浏览器返回一个响应 cookie,用来保存 sessioin 的ID - connect.sid = sessionID
在服务器通过 request.session 获取到 session
session 如果有: 找到对应的 session 对象
session 如果不存在: 创建新的 session 对象
第一次登录,会将 user_id 存入 session
刷新浏览器, user_id 还是可以从 浏览器获取的
但是 session 通常与 cookie 进行连用
- 会话 cookie ---- 关闭浏览器,则数据清空
- 持久化 cookie ---- 关闭浏览器,数据还在
-------------------------------------------------------------------------------------
- mini-ui
npm install --save mint-ui
npm install --save-dev babel- // 实现按需打包
/src/mock/data.json
处理后台接口还不可使用的情况,拦截 ajax ,返回 模拟数据
/src/mock/mockServer.js -------- 使用 mockjs 来 mock 模拟数据接口
import Mock from "mockjs"
import data from "./data.json" -------- 内部会自动解析 JSON 对象 成 js 对象
// mock 三个接口,开始监视改变, 特别的是这个代码不会向外暴露什么
Mock.mock("/goods", {code: 0, data: data.goods})
Mock.mock("/ratings", {code: 0, data: data.ratings})
Mock.mock("/info", {code: 0, data: data.info})
/src/main.js
import "./mock/mockServer" // 保证 mockjs 被打包执行,从而组件可以使用
4
4
- Vuex.Store 模块化
管理的状态
{home:???, user:???, shop:???}
/vuex/store/modules/
home.js
- 1
/src/vuex/store/index.js
import home from "./modules/home"
import user from "./modules/user"
import shop from "./modules/shop"
export default new Vuex.Store({
modules: {
home,
user,
shop
}
})
npm install --save better-scroll
new BScroll(".wrapper", {})
-
mounted () { this.$nextTick(()=>{ if(!this.bScroll){ this.bScroll = new BScroll(".header_nav", { click: true, scrollX: true }) }else{ this.bScroll.refresh() } }) }
- ...mapGetter(["xxx"])
模块化 时,无论写在模块内还是公共 getter,都是一样的,
只是在编写 getter 时,注意不能重名,和 state 的指向
- 单例对象
某种类型的实例对象只存在一个
在轮播图 逻辑的元素,在控制外层元素时,只能用 v-show 而不能用 v-if
- moment 自定义过滤器 - 日期处理 /src/filters/index.js
import Vue from 'vue'
import moment from "moment"
Vue.filter('data-format', funtion(vue, formatStr="YYYY-MM-DD HH:mm:ss"){
return moment().format(formatStr)
})
- 优化 router ---- 路由懒加载效果
1. 使用 import 函数: 被引入的模块单独打包 (生成有一个单独的一个 js 文件)
2. 配置的 component 是“返回 import() 得到的模块的函数”, 只有当请求对应的 path 才会执行函数,得到组件
- 图片懒加载 vue-lazyload ---- 其实 mint-ui 内置了这个库
- main.js
---- 内部会指定一个指令 lazy
- 在组件中 <img v-lazy="food.image"/>
- 构建打包app
1. 无 # 哈希路由 ---- 配置 404 页面, 将 404 页面指向 目标页面 index.html ---- 单页面应用
在页面刷新,就会向后台请求路由,而路由是前台路由
将服务器进行 中间件拦截 - 将前台路由请求 返回 public 页面
2. 使用 /#/ 哈希路由 ---- 始终会被当成前台路由处理,而不会当成后台路由处理
- 将 store 保存到 Vue 上 ---- 很实用
- 导航守卫
是 vue-router 提供的 下面 2 个方面的功能
- 监视路由跳转
- 控制路由跳转
应用
在跳转到界面之前,对用户进行检查限制
在界面离开之前进行收尾工作
分类
全局守卫 ---- 针对任意路由跳转
/src/router/index.js
-
import Vue from "vue"; import VueRouter from "vue-router"; // import Home from "../pages/Home/Home" import Shop from "../pages/Shop/Shop" import Goods from "../pages/Shop/Goods/Goods" import Ratings from "../pages/Shop/Ratings/Ratings" import Info from "../pages/Shop/Info/Info" // import Search from "../pages/Search/Search" // import Order from "../pages/Order/Order" // import Personal from "../pages/Personal/Personal" import LoginRegister from "../pages/LoginRegister/LoginRegister" const Home = ()=>import("../pages/Home/Home") const Search = ()=>import("../pages/Search/Search") const Order = ()=>import("../pages/Order/Order") const Personal = ()=>import("../pages/Personal/Personal") Vue.use(VueRouter); const router = new VueRouter({ mode: "history", routes: [ {path:"/home", component: Home, meta:{showFooter: true}}, { path:"/shop", component: Shop, children: [ {path:"/shop/goods", component: Goods}, {path:"/shop/ratings", component: Ratings}, {path:"/shop/info", component: Info}, {path:"/shop", redirect: "/shop/goods"}, ] }, {path:"/search", component: Search, meta:{showFooter: true}}, {path:"/order", component: Order, meta:{showFooter: true}}, {path:"/personal", component: Personal, meta:{showFooter: true}}, {path:"/login_register", component: LoginRegister}, {path:"/", redirect: "/home"}, ] }) /*************************************************/ const paths = ["/login_register"] router.beforeEach((to, from, next)=>{ // 设置全局守卫 const path = to.path if(paths.indexOf(path)>=0){ if(Vue.store.state.user.userInfo._id){ // 在 main.js 中保存 store return next("/personal") } } next() // 其他路由请求,放行 }) /*************************************************/ export default router
- 全局前置守卫: 在准备跳转到某个路由组件之前 (在开发中用的较多)
router.beforeEach((to, from, next)=>{
})
to ---- 目标路由
from ---- 当前路由
next ---- 函数
next() ---- 执行下一个守卫回调,如果没有,就跳转到目标路由
next(false) ---- 不继续执行,跳转流程在此处中断,不会跳转到目标路由组件
next(path) ---- 跳转到指定的另一个路由
next() ---- 传入的回调函数会在组件对象创建后对象,即延后执行回调 - 且将组件对象传入回调函数 即 this
- 全局后置守卫: 在跳转到某个路由组件之后
router.afterEach((to, from)=>{
})
组件守卫 ----
beforeRouteEner(){ // 在跳转到当前组件之前,无法访问这个组件的组件对象 this
}
beforeRouteUpdate(){ // 在当前组件显示更新前调用,可以访问 this
}
beforeRouteLeave(){ // 在当前组件离开前调用,可以访问 this
}
- 构建打包
- 配置前台路由 - 404 页面
/src/router/index.js
在数组最后,配置{path: "/*", NotFound}