vue 购物 WebApp
项目概述
简介
项目名 蘑菇购,与一般购物WebApp
类似,包括首页、分类、购物车、个人中心、详情。
项目基于vue
、vue-router
、vue-cli3
,api
请求相关部分采用axios
,数据部分并非来自服务器,而是本地基于express
启动相关数据服务。原因一是网络接口更新快、数据变化大、依赖性高,二是项目本身不大,基于项目启动本机服务灵活性较高,代码安装依赖即可运行,故最终考虑express
爬取相关接口数据保存本地。
状态管理未使用vuex
,仅仅是少部分使用vuex
功能显得多余,项目大才完全有必要。基于vue.observable
能实现部分vuex
功能。图片加载部分异步更新DOM
采用事件总线进行组件通讯。
项目第三方开源组件包括better-scroll
滚动插件、vue-awesome-swiper
和swiper
轮播组件、normalize.css
初始化样式、vue-lazyload
懒加载、移动端click300ms
延时采用fastclick
。
项目难度不高,适合新手练手,此篇仅是练习组件化封装和目录配置的相关记录。
预览地址
GitHub / Gitee
示例图
文件目录配置
├── public
│ ├── favicon.ico
│ ├── index.html
├── server
│ ├── static
│ │ ├── image
│ ├── app.js
│ ├── db.js
│ ├── router.js
├── src
│ ├── api
│ │ ├── home.js
│ │ ├── category.js
│ ├── assets
│ │ ├── iconfont
│ │ ├── img
│ │ ├── placeholder.png
│ ├── components
│ │ ├── BetterScroll
│ │ ├── CheckButton
│ │ ├── IndexBar
│ │ ├── Message
│ │ │ ├── Message.vue
│ │ │ ├── index.js
│ │ ├── Navbar
│ │ ├── Swiper
│ │ ├── SwiperSlide
│ │ ├── Tabbar
│ │ ├── TabbarItem
│ ├── layout
│ │ ├── Tabbar
│ ├── router
│ │ ├── index.js
│ │ ├── routes.js
│ ├── store
│ │ ├── index.js
│ │ ├── vuex.js
│ ├── styles
│ │ ├── index.less
│ ├── utils
│ │ ├── index.js
│ │ ├── request.js
│ ├── views
│ │ ├── home
│ │ ├── category
│ │ ├── cart
│ │ ├── profile
│ │ ├── detail
│ ├── App.vue
│ ├── main.js
│ ├── .env.development
│ ├── package.json
│ ├── README.md
│ ├── vue.config.js
初始化
脚手架初始化
初始空脚手架vue-cli3
仅配置Babel
、Router
、CSS Pre-processors
(less
),删除其余业务不相关部分,文件夹部分通过需求逐步新建。
Tabbar 组件、路由
项目目前正常运行为空白,先搭建路由相关部分,抽离routes
静态数据,同级目录下新增routes.js
导出静态数据,index.js
引入静态数据。
// router -> index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'Vue.use(VueRouter)const router = new VueRouter({mode: 'hash',routes,
})export default router// router -> routes.js
export default [{path: '/',redirect: '/home',},{path: '/home',name: 'home',component: () => import('views/home'),},...
]
项目启动,会发生路由路径加载错误,需要配置文件夹别名,空脚手架不含vue.config.js
,需要手动新增。路径src/views
修改别名views
,其余别名后续会用到,全部配置。
// vue.config.js
const path = require('path')function resolve(dir) {return path.join(__dirname, dir)
}module.exports = {chainWebpack: config => {config.resolve.alias.set('@', resolve('src')).set('views', resolve('src/views'))...},
}
文件夹别名配置后,懒加载路径下并没有文件。views
新增home
文件夹,其下新增index.vue
,其余文件同理,重启运行。
此时项目依旧空白,但是home
下index.vue
已被重定向,接下来封装Tabbar
,Tabbar
较为公共,components
下新建Tabbar
、TabbarItem
,文件夹下均新增index.vue
。Tabbar
一般高度49px
最为舒适,同时定位屏幕底部,层级高于其他组件。TabbarItem
内部引入router-link
,组件接收参数参考 vant-ui 并做了部分修改,通过当前routes.path
参数和计算属性配置高亮。
// Tabbar -> index.vue
<div class="tabbar"><slot />
</div>// TabbarItem -> index.vue
<div class="tabbar-item"><router-link :to="to" tag="div"><div class="tabbar-item-icon" :style="{ color: to === path ? activeColor : inactiveColor }"><slot name="icon" /></div><div class="tabbar-item-text" :style="{ color: to === path ? activeColor : inactiveColor }"><slot name="text" /></div></router-link>
</div>export default {props: {to: {type: String,default: '',},activeColor: {type: String,default: '',},inactiveColor: {type: String,default: '',},},computed: {path() {return this.$route.path},},
}
公共组件Tabbar
封装完成,项目相关Tabbar
还未封装及引用。由于项目相关Tabbar
有关于项目页面布局。故src
下新增layout
文件夹,相关布局组件不会太多,不用文件夹下再放index.vue
形式。
// layout -> Tabbar.vue
<common-tabbar><common-tabbar-item to="/home" active-color="#ff8198" inactive-color="#555555"><i slot="icon" class="iconfont icon-home"></i><span slot="text">首页</span></common-tabbar-item>...
</common-tabbar>import CommonTabbar from 'components/Tabbar'
import CommonTabbarItem from 'components/TabbarItem'export default {components: {CommonTabbar,CommonTabbarItem,},
}
图标采用 iconfont,官网选择合适的Tabbar
图标,下载压缩包解压。assets
文件夹下新建iconfont
文件夹,引入解压的全部文件。其中demo_index.html
关于字体图标使用方式做了详细阐述,iconfont.css
需要手动引入,iconfont
也是一种字体,最终归结为css
样式。src
下新建styles
文件夹,创建index.less
,index.less
放置公共初始化样式,main.js
最终引入index.less
。
├── iconfont
│ ├── demo_index.html
│ ├── demo.css
│ ├── iconfont.css
...// index.less
@import '~assets/iconfont/iconfont.css';// main.js
import 'styles/index.less'
Tabbar
业务组件封装完成,App.vue
引入,项目运行下Tabbar
展示屏幕底部,点击Tabbar
发生路由跳转和URL
更新。
// App.vue
<div id="app"><tabbar /><router-view />
</div>import Tabbar from 'layout/Tabbar'export default {components: { Tabbar },
}
样式、页面标题、图标初始化
路由重定向至home
页面,发现body
存在margin
,安装normalize.css
,mian.js
引入。
// 安装
npm i normalize.css --save// main.js
import 'normalize.css'
styles
文件夹下index.less
,初始化html
、body
、#app
高度,删除App.vue
相关样式。
html,
body,
#app {height: 100%;
}
路由添加导航守卫router.beforeEach
,用于初始化页面标题,但是目标路由to
并不含有meta.title
,修改routes.js
,其余同理,正确运行页面标题切换。替换public
下favicon.ico
,项目刷新显示图标。
// router -> index.js
router.beforeEach((to, from, next) => {document.title = to.meta.titlenext()
})// router -> routes.js
{path: '/home',name: 'home',meta: {title: '首页',},component: () => import('views/home'),
}
NavBar
NavBar
也是一般较通用组件,components
新建NavBar
,组件开放插槽,一般高度44px
最为舒适,路由页面、详情均使用,组件传值background-color
。home
页引入,页面引入组件顺序遵循引入公共组件、定制组件、公共js
、定制js
。
数据服务
Express
项目目前可实现路由跳转,相关api
以及数据还未准备。网络接口常更新、数据不稳定,采用express
、superagent
爬取保存接口数据,爬虫crawler
参考其他文章。大致拆分项目需要用到的后端接口,首页轮播图、特色、推荐、详情、列表、分类等,项目新建serve
文件夹。image
保存数据图片。
├── serve
│ ├── static
│ │ ├── image
│ ├── app.js
│ ├── db.js
│ ├── router.js
│ ├── const.js
│ ├── utils.js
app.js
启动数据服务,开放静态static
文件夹,映射/static
到serve/static
。
app.use('/static/', express.static('./serve/static/'))
db.js
本地数据库,baseURL
为本机局域网ip
,便于移动端访问本机数据,也方便调试。项目之前使用ipconfig
手动输入的方式,这种方式不免显得繁琐,数据服务一启动便与项目没有实际关联性,没有必要再去修改一次ip
地址。故使用os
模块动态获取本机局域网ip
,当然此种方式如若PC
端访问图片失败,大概率是动态获取ip
部分有误,注释相关代码,通过上一种方式修改ip
即可。
const os = require('os')
const interfaces = os.networkInterfaces()
const port = 3000
const baseURL = 'http://127.0.0.1:3000'for (const key of Object.keys(interfaces)) {const el = interfaces[key].find(el => el.family === 'IPv4' && el.address !== '127.0.0.1')el && (baseURL = `http://${el.address}:${port}`)
}
router.js
后端路由部分,由于业务相关接口不是特别多,不用router
再去分级,也不存在post
相关请求,不需要额外安装body-parser
。
const db = require('./db')
const { SUCCESS_CODE } = require('./const')router.get('/api/getBann', (req, res) => {res.json({code: SUCCESS_CODE,content: db.banner,msg: 'success',})
})
...
package.json
配置快速启动命令。
// 安装
npm i nodemon --save-dev"scripts": {"serve": "nodemon serve/app.js",
},
axios、api 封装
项目使用axios
第三方插件,安装步骤参考 axios。
├── src
│ ├── api
│ │ ├── home.js
│ ├── utils
│ │ ├── request.js
├── .env.development
├── vue.config.js
request.js
放置在utils
工具类函数文件夹下。
const server = axios.create({timeout: 5000,
})
开发与产品URL
一般不一致,通常是配置环境变量,根目录创建.env.development
文件,后期需要添加.env.production
配置产品环境变量。
VUE_APP_BASE_API = '/api'
项目下尝试访问express
请求通常情况会发生跨域报错,服务端可设置跨域部分,或者项目设置代理。
// vue.config.js
devServer: {port: 8000,open: true,proxy: {[process.env.VUE_APP_BASE_API]: {target: 'http://127.0.0.1:3000/',ws: false,changeOrigin: true,},},
},
引入request.js
,设置请求url
、请求方式,页面引用。
// api.js => home.js
import request from 'utils/request'export function getBann() {return request({url: '/api/getBann',method: 'get',})
}// 引用页面
import { getBann } from 'api/home'getBann().then(res => { ... }).catch(err => { ... })
BetterScroll、vue-awesome-swiper
项目涉及第三方组件主要是BetterScroll
、vue-awesome-swiper
,BetterScroll
也是公共组件,components
新建BetterScroll
文件夹,详细步骤参考 better-scroll。swiper
也是较为公共的组件,components
新建Swiper
、SwiperSlide
,vue-awesome-swiper
版本造成的坑比较多,主要由于vue-awesome-swiper
与swiper
的版本不适应造成,建议使用4.1.1
和5.2.0
,详细步骤参考 vue-awesome-swiper。
页面
首页
目前基础架子基本搭建完成,vuex
状态管理部分暂不考虑,实际用到的时候自然带入。首页组件已含有NavBar
,调整首页目录结构,组件命名尽量语义化,后期维护非常方便。
├── home
│ ├── index.vue
│ ├── components
│ │ ├── RecommendView.vue
│ │ ├── FeatureView.vue
│ │ ├── CardList.vue
│ │ ├── CardListItem.vue
首页组件树结构,浏览器安装devtools
工具非常直观。
▼<Home><NavBar>▼<BetterScroll>▼<Swiper><SwiperSlide><RecommendView><FeatureView><IndexBar>▼<CardList><CardListItem>
NavBar
默认fiexed
定位屏幕顶部,会导致遮住better-scroll
,home
使用伪元素before
规避。且better-scroll
外层wrapper
需要指定高度,尽量加上相对定位。
// styles -> index.less
.m-home::before {content: '';display: block;height: 44px;width: 100%;
}// home -> index.vue
.scroll {height: calc(100vh - 93px);overflow: hidden;position: relative;
}
轮播图获取等数据接口,页面调用都需要api
文件夹文件声明接口再引入。
// api -> home.js
export function getRecom() {return request( ... )
}// home -> index.vue
import { getBann } from 'api/home'
IndexBar
也是公共组件,components
新建IndexBar
,组件参数传递数组,存在高亮切换和点击事件的抛出,同时含默认高亮,则将IndexBar
封装v-model
形式。props
增加组件可复用性,不仅仅只依赖于data
内数据label-value
对形式,传递props
可依赖多种形式。model
、props.data
是封装自定义组件v-model
必备,具体步骤参考官方 v-model,index-bar-item
点击调用change
,实现v-model
。
// IndexBar -> index.vue
<divv-for="item in data":key="item[props.value]"class="index-bar-item":class="{ active: value === item[props.value] }"@click="itemClick(item)"
>...
</div>export default {model: {value: 'value',event: 'change',},props: {data: {type: Array,default: () => [],},value: {type: String,default: '',},props: {type: Object,default: () => ({label: 'label',value: 'value',}),},},methods: {itemClick(item) {item[this.props.value] !== this.value && this.$emit('change', item[this.props.value])},},
}// home -> index.vue
<index-bar v-model="currentBar" :data="indexBars" @change="onChange" />data: {indexBars: [{label: '流行',value: '0',},...],currentBar: '0',
},
列表数据接口,传递参数包括点击currentType
、pageNum
、pageSize
。图片异步加载必然导致better-scroll
高度计算失误,每张图片加载完毕都要重新计算高度才合理,故CardListItem
内图片load
完毕需要抛出给首页,再调用scroll
组件内refresh
方法。首页与CardListItem
组件之间的关系薄弱,或者说没有关系,组件间事件通信可采用EventBus
事件总线的方式。
// mian.js
Vue.prototype.$bus = new Vue()// CardListItem 发出
onLoad() {this.$bus.$emit('imageLoad')
},// home -> index.vue 监听
this.$bus.$on('imageLoad', () => {this.$refs.scroll.refresh()
})
但是对于图片较多的列表,会导致调用refresh
方法频繁,需要添加防抖函数。utils
下index.js
,timer
作为了闭包函数debounce
的私有变量,首页引入函数debounce
。
export function debounce(func, delay = 20) {var timer = nullreturn function (...arg) {if (timer) clearTimeout(timer)timer = setTimeout(() => {func.apply(this, arg)}, delay)}
}
当组件scroll
实例完全创建完毕才有必要生成防抖函数,实例未创建完毕$ref.scroll.refresh
不存在,生成的防抖函数实际也不生效,短路运算&&
更加保证refresh
非函数则不执行。如此fresh
就是一个保存有私有变量timer
的防抖函数,图片加载小于20ms
只执行最后一次。
// home -> index.vue
<scroll @load="onLoad" />onLoad() {this.refresh = debounce(this.$refs.scroll.refresh, 20)
},mounted() {this.$bus.$on('imageLoad', () => {this.refresh && this.refresh()})
},
indexBar
吸顶,通过使better-scroll
下的InddexBar
fixed
定位不可取,better-scroll
使用translate
会导致内部定位元素非理想状态,解决办法最好是NavBar
同级再添加组件IndexBar
fixed
定位,scroll
未到吸顶距离隐藏,吸顶距离则显示。showTop
用于返回顶部,滚动距离高于一屏则显示返回顶部按钮。
// home -> index.vue
scroll({ y }) {this.$nextTick(() => {this.showSticky = this.$refs.indexBar && -y > this.$refs.indexBar.$el.offsetTop})this.showTop = -y > document.body.clientHeight
},
上拉加载、下拉刷新、IndexBar
切换,下拉重新调用接口,上拉当前pageNum++
,再获取数据,list
数据使用concat
拼接,或者使用push(...array)
方式,indexBar
切换重新获取数据,scroll
滚动至IndexBar
位置。
this.$nextTick(() => {this.showSticky && this.$refs.scroll.scrollTo(0, -this.$refs.indexBar.$el.offsetTop, 0)
})
首页需要keep-active
缓存,保存页面状态。
// App.vue
<keep-alive><router-view />
</keep-alive>
详情
路由routes.js
新增详情路由。
// router -> routes.js
{path: '/detail/:id',name: 'detail',meta: {title: '详情',},component: () => import('views/detail'),
},// home -> index.vue
this.$router.push({ path: `/detail/${id}` })
目录结构。
├── Detail
│ ├── index.vue
│ ├── components
│ │ ├── GoodsInfo.vue
│ │ ├── StoreInfo.vue
│ │ ├── ClothList.vue
│ │ ├── ParamsInfo.vue
│ │ ├── CommentList.vue
│ │ ├── RecommendList.vue
│ │ ├── NavBar.vue
│ │ ├── SubmitBar.vue
组件树结构。
▼<Detail><NavBar>▼<BetterScroll>▼<Swiper><SwiperSlide><GoodsInfo><StoreInfo><ClothList><ParamsInfo><CommentList><RecommendList><SubmitBar>
组件大致同首页一致,NavBar
差别较大,NavBar
对公共组件的NavBar
进行封装,组件自定义v-model
,抛出change
事件,点击实现类似锚点的功能,同时伴随高亮。大致原理点击获取元素的value
值,value
值查询navbars
对应的refName
,获取对应组件的offsetTop
实现锚点。
navbars: [{label: '商品',value: '0',refName: 'swiper',},
],this.$refs.scroll.scrollTo(0, -this.$refs[refName].$el.offsetTop)
scroll
滚动过程中高亮伴随切换,在scroll
事件中获取滚动距离,遍历navbars
设置currentBar
的值,同时v-model
双向绑定currentBar
,从而实现滚动高亮。
this.navbars.forEach(el => {if (this.$refs[el.refName] && -y >= this.$refs[el.refName].$el.offsetTop) {this.currentBar = el.value}
})
添加购物车需要vuex
状态管理,需要用到的部分实质只有购物车的商品列表,故使用vuex
显得大材小用,况且不用vuex
也能实现迷你版状态管理。为了保留与vuex
一致性,store
下新增index.js
、vuex.js
,vuex.js
声明Store
类,构造函数默认观察state
数据。
import Vue from 'vue'class Store {constructor({ state, mutations }) {Object.assign(this, {state: Vue.observable(state || {}),mutations,})}commit(type, arg) {this.mutations[type](this.state, arg)}
}export default { Store }
index.js
与一般状态管理基本一致。
import Vuex from './vuex'export default new Vuex.Store({state: {goods: [],},mutations: {ADD_GOODS(state, arg) { ... },ALL_CHECKED(state, val) { ... },},
})
页面实现this.$store
方式调用还要将导出实例放置原型上,至此迷你版vuex
调用方式与vuex
趋于一致,actions
、gutters
暂时用不上。
import store from './store'Vue.prototype.$store = store
添加购物车按钮点击,调用mutations
方法。
this.$store.commit('ADD_GOODS', { ... })
详情页面点击不同首页商品,只会请求同一商品,原因keep-active
缓存了当前详情页,不会再次触发created
,调整App.vue
。
<keep-alive exclude="Detail"></keep-alive>
此时详情页Tabbar
还存在,类比keep-active
,组件传值exclude
。
// layout -> Tabbar.vue
export default {props: {exclude: {type: String,default: '',},},computed: {show() {const excludes = this.exclude.split(',')return !excludes.includes(this.$route.name)},},
}// App.vue
<tabbar exclude="detail" />
Message
消息提示组件封装,根据开源组件库 element-ui,封装一个简单版的Message
,components
下新建Message
,新建main.vue
、index.js
,main.vue
内部mounted
之后,固定延时关闭Message
,同时执行关闭回调。
<transition v-if="visible" name="fade"><div class="message">{{ message }}</div>
</transition>export default {data() {return {visible: true,message: '',duration: 2000,onClose: null,}},mounted() {setTimeout(() => {this.visible = falsethis.onClose && this.onClose()}, this.duration)},
}
index.js
内部引入Vue
,同时引入组件Message
,创建组件构造器,通过new
构造器创建组件实例,$mount
挂载当前实例同时渲染为真实DOM
,再追加至body
内部,对外抛出install
方法。
import Vue from 'vue'
import main from './main.vue'const MessageConstructor = Vue.extend(main)const Message = function (options) {if (typeof options === 'string') {options = {message: options,}}const instance = new MessageConstructor({data: options,})instance.$mount()document.body.appendChild(instance.$el)
}export default {install() {Vue.prototype.$message = Message},
}
main.js
引入组件,Vue.use()
调用内部install
方法,Message
被置于Vue.prototype
上。
// mian.js
import Message from 'components/Message'
Vue.use(Message)// detail -> index.vue
this.$message('商品添加成功!')
购物车
购物车页面商品多,存在滚动情况,使用better-scroll
,页面列表依赖store
内state
。
computed: {data() {return this.$store.state.goods},
},
目录结构。
├── cart
│ ├── index.vue
│ ├── components
│ │ ├── GoodsList.vue
│ │ ├── TotalBar.vue
组件树结构。
▼<Cart><NavBar>▼<BetterScroll>▼<GoodsList><CheckButton>▼<TotalBar><CheckButton>
CheckButton
即公共选中按钮,components
下新建CheckButton
,内部实现v-model
,内部通过切换背景色实现选中和取消,且内部点击事件阻止冒泡。可能存在当外部调用CheckButton
时,带有CheckButton
的整个卡片点击则CheckButton
取消或者选中,此时修改v-model
绑定值即可。但是当点击CheckButton
时,由于本身CheckButton
被点击时会切换,加上事件冒泡,外层卡片也会触发点击事件,再次修改v-model
值,出现预期之外的结果,最好的办法就是阻止事件的冒泡。
@click.stop="$emit('change', !value)"
TotalBar
内部计算属性依赖store
内state
,根据state
商品数量动态计算价格、总量。全选按钮点击商品全部选中,再次点击全部取消。全选点击则调用store
下mutations
遍历修改商品checked
属性。但是点击CheckButton
,由于内部冒泡的阻止,触发不了外部点击事件。此时伪元素after
就又能派上用场了,定位一个空盒子在全选按钮上,点击事件的触发元素一直是这个after
伪元素。
.check {position: relative;&::after {content: '';display: block;position: absolute;left: 0;right: 0;top: 0;bottom: 0;}
}
由于keep-active
的缓存机制,导致列表无法下拉,主要由于初始情况scroll
计算高度错误导致。解决办法一,添加activated
事件,页面活动时,调用组件内部refresh
事件更新高度。
activated() {this.$nextTick(() => {this.$refs.scroll.refresh()})
},
解决办法二,keep-active
不缓存cart
页面。
<keep-alive exclude="Cart,Detail"></keep-alive>
分类
目录结构。
├── category
│ ├── index.vue
│ ├── components
│ │ ├── CatesList.vue
组件树结构。
▼<Category><NavBar><CatesList>
个人信息
目录结构。
├── profile
│ ├── index.vue
│ ├── components
│ │ ├── UserInfo.vue
│ │ ├── CountInfo.vue
│ │ ├── OptionList.vue
组件树结构。
▼<Profile><NavBar><UserInfo><CountInfo><OptionList>
优化部分
图片懒加载
首页商品懒加载,assets
文件夹添加懒加载填充图。
// 安装
npm i vue-lazyload --save// main.js
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {loading: require('assets/placeholder.png'),
})
移动端点击
移动端300ms
点击。
// 安装
npm i fastclick --save// main.js
import FastClick from 'fastclick'
FastClick.attach(document.body)
px 转换 vw
px
转vw
视口单位,相关插件 postcss-px-to-viewport,根目录需要新建postcss.config.js
配置文件,相关配置参数官方文档很详尽了,唯一需要注意的就是px
单位避免存在于行内/内联样式,minPixelValue
最小转换数值一般为1
,可能有部分边框需要1px
显示。
// 安装
npm install postcss-px-to-viewport --save-dev// postcss.config.js
module.exports = {plugins: {'postcss-px-to-viewport': {unitToConvert: 'px',viewportWidth: 375,unitPrecision: 6,propList: ['*'],viewportUnit: 'vw',fontViewportUnit: 'vw',selectorBlackList: [],minPixelValue: 1,replace: true,exclude: undefined,include: undefined,landscapeUnit: 'vw',},},
}
windows nginx 部署
nginx 选择Stable version
稳定版nginx/Windows-x.xx.x
,下载压缩包解压,根目录执行命令启动nginx
。
// 查看 nginx 版本号
nginx -v// 启动
start nginx// 强制停止或关闭 nginx
nginx -s stop// 正常停止或关闭 nginx (处理完所有请求后再停止服务)
nginx -s quit// 修改配置后重新加载
nginx -s reload// 测试配置文件是否正确
nginx -t
浏览器输入http://localhost/
,正常访问为Welcome to nginx!
,nginx
默认访问html/index.html
,可修改配置文件conf/nginx.conf
更改默认路径,运行重新加载命令。
├── dist
│ ├── index.html
│ ├── ...
├── html
│ ├── index.html
│ ├── 50x.html
...location / {root dist;index index.html index.htm;
}
后记
项目基本思路均梳理大半,部分思路可能未提及,项目 GitHub 开放,可以克隆或者下载压缩包,仓库内存稍大,大约464M
,压缩包下载1
分钟左右,原因主要由于脱离网络接口,数据保存本地导致,详细情况开头已细致说明。整个项目非常适用新手练手,服务端数据服务只需要npm run serve
即可开启。
由于express
动态获取本机内网ip
,所以完全可以手机访问cli-service
启动的Network
地址,实现手机浏览器也可预览的效果。
更新日志
2020/11/13 10:31
图片存放项目中首次下载或克隆耗时太长,express
也是获取本机局域网ip
实现移动访问,项目显得比较冗余。倘若有一个图床,express
负责返回不同图片地址,问题会得到根本程度的解决。于是利用GitHub
,手动造一个图床,了解原理不用网上的PicGo
也能实现。
实质就是开辟一个GitHub
公开仓库,提交图片文件即可。仓库内部预览图片,点击原始数据获取图片原始URL
,图片根目录一般是https://github.com/用户名/仓库名/raw/master
,剩余部分则是图片在项目中的路径。
注意
GitHub
图床不太稳定,图片经常会挂掉,加速地址访问也会挂掉。项目内目前使用的是Gitee
图床,相对会稳定很多。
删除掉原项目动态获取局域网ip
,app.js
内静态文件关闭,删除static
文件夹。图片详情推荐随机数生成可能存在相同情况优化。
2022/03/05 17:55
项目有云服务器是可以实现访问并预览的,但是小项目练手没有必要,GitHub
开放了静态网页预览功能,可以调整部分代码实现。ajax
部分去掉,api
内不引入request
工具函数,直接引入serve
下db.js
,合并router.js
和api
下函数。
实现浏览器静态网页的代码提交在browser
分支,静态Page
部署在GitHub
,预览较慢,镜像页面也可以在 Gitee 访问。
vue 购物 WebApp相关推荐
- Vue购物商城项目(二) 数据请求使用
Vue购物商城项目(二) 文章目录 Vue购物商城项目(二) 前言 一.请求数据 request.js home.js Home.vue 二.使用数据 总结 前言 1.这里面包含了大量的.我的个人理解 ...
- vue实现webapp
最近看了点vue框架,关于它的资料和angular相比还是比较少的,但是官网的文档很全面,想要了解的知识点文档上基本都有,所以就看了下文档,一边学习一边写了个小程序,觉得vue还是很好上手的.大大的提 ...
- 基于Vue的WebApp项目开发(四)
实现新闻咨询页面 目录结构 步骤一:创建newslist.vue文件 <template><div id="tml"><!--使用mui框架,实现新闻 ...
- 爱前端邵山欢课程 node+angular+vue+react+webapp高级部分
[邵老师]大前端爱前端学习课程初中高级课程全套,课程很全面,名师讲座,由博学会员分享,未一一检测是否可用,大家可选择学习 北京航空航天大学软件工程硕士,前端开发培训名师.讲课幽默.有***,学生称他的 ...
- 从零开始配置Vue项目(WebApp)
1. 组件初始化安装 全局安装 vue-cli npm install –-global vue-cli 创建一个基于webpack模板的新项目 vue init webpack my-project ...
- vue 地理位置定位_cordova+vue webapp 使用html5获取地理位置
1.在HTML5中使用Geolocation.getCurrentPosition()方法来获取地理位置. 语法: navigator.geolocation.getCurrentPosition(s ...
- Awesome Vue.js vue.js学习资源链接大全 中文
策划列表相关的vue.js可畏的事 资源 官方资源 外部资源 工作门户 社区 会议 播客 官方的例子 教程 实例 书 项目采用vue.js 开源 商业产品 应用/网站 互动体验 企业使用 组件& ...
- sku属性组合小例子
参考: http://www.cnblogs.com/hsp-blog/p/6215721.html http://blog.csdn.net/csdn924618338/article/detail ...
- yoman不压缩html,使用Yeoman构建vuejs
之前使用AngularJs的时候,一直用Yeoman构建项目,感觉还不错.接下里的项目定位使用轻量型的框架,所以最近开始学习vuejs,这里插一句题外话,与facebook这帮人写的react教程相比 ...
- 浏览器及app消息推送
消息推送 什么是消息推送 PC端的实现 方法1:Notification 方法2:pushjs APP端 实现 打包设置 什么是消息推送 消息推送可以存在于浏览器端,也存在APP端.浏览器的推送,会在 ...
最新文章
- 软件工程技术基础-(软件复用技术)
- 系统维护For流星无语
- GUI编程与CLI编程
- 关于File.separator 文件路径:window与linux下路径问题(“No such file or diretory ”异常解决方案)...
- 【转载】Linux free 查询可用内存和判断是否有内存泄漏
- 【寻子】人脸识别与寻子的碰撞
- 将MFC Grid Control封装为DLL的做法及其在DLL中的使用方法
- @property 参数问题
- 常用视频格式简述(RMVB\RM\WMV\ASF\AVI\MPEG1\MPEG2\MPEG4\MOV)
- 区块链入门教程 。阮一峰
- 紫光华宇拼音输入法使用技巧
- 易语言制作的QQ聊天中常用的GIF图片【带源码下载】
- text 热敏打印机_GitHub - huangzhiyi/thermal_printer: Java实现网络小票打印机自定义无驱打印...
- 计算机论文参考文献范文,计算机文类论文参考文献 计算机文参考文献有哪些...
- 阿里云ACP认证之云安全知识整理(考题占比 10%)
- 计算机学术引用论文,我国计算机领域学术论文引用中的马太效应——以《计算机学报》和《计算机研究与发展》为例...
- 【读书笔记】销售运营-策略制定的6大原则及5个常用工具
- 探寻红色印记,传承红色基因
- Tiny4412裸机程序之代码重定位初体验
- SSO 轻量级实现指南(原生 Java 实现):SSO Client 部分
热门文章
Vue购物商城项目(二) 文章目录 Vue购物商城项目(二) 前言 一.请求数据 request.js home.js Home.vue 二.使用数据 总结 前言 1.这里面包含了大量的.我的个人理解 ...
最近看了点vue框架,关于它的资料和angular相比还是比较少的,但是官网的文档很全面,想要了解的知识点文档上基本都有,所以就看了下文档,一边学习一边写了个小程序,觉得vue还是很好上手的.大大的提 ...
实现新闻咨询页面 目录结构 步骤一:创建newslist.vue文件 <template><div id="tml"><!--使用mui框架,实现新闻 ...
[邵老师]大前端爱前端学习课程初中高级课程全套,课程很全面,名师讲座,由博学会员分享,未一一检测是否可用,大家可选择学习 北京航空航天大学软件工程硕士,前端开发培训名师.讲课幽默.有***,学生称他的 ...
1. 组件初始化安装 全局安装 vue-cli npm install –-global vue-cli 创建一个基于webpack模板的新项目 vue init webpack my-project ...
1.在HTML5中使用Geolocation.getCurrentPosition()方法来获取地理位置. 语法: navigator.geolocation.getCurrentPosition(s ...
策划列表相关的vue.js可畏的事 资源 官方资源 外部资源 工作门户 社区 会议 播客 官方的例子 教程 实例 书 项目采用vue.js 开源 商业产品 应用/网站 互动体验 企业使用 组件& ...
参考: http://www.cnblogs.com/hsp-blog/p/6215721.html http://blog.csdn.net/csdn924618338/article/detail ...
之前使用AngularJs的时候,一直用Yeoman构建项目,感觉还不错.接下里的项目定位使用轻量型的框架,所以最近开始学习vuejs,这里插一句题外话,与facebook这帮人写的react教程相比 ...
消息推送 什么是消息推送 PC端的实现 方法1:Notification 方法2:pushjs APP端 实现 打包设置 什么是消息推送 消息推送可以存在于浏览器端,也存在APP端.浏览器的推送,会在 ...