项目开发流程
脚手架
一、自己搭建(公司搭建属于自己的) webpack
二、vue-cli webpack3
三、@vue/cli webpack4
如果你自己的电脑中的是vue-cli,企业用的是@vue/cli
cnpm uninstall vue-cli -g
cnpm install @vue/cli -g
vue create myapp
如果你用的是@vue/cli,但是想用vue-cli创建项目
cnpm install @vue/cli-init -g
vue init webpack myapp
以vue-cli项目为例
1、index.html文件处加入如下代码,以解决移动端点击穿透事件以及不支持promise的情况,如果是PC端项目,跳过此步
2、修改目录结构
src
api
assets
components
lib
router
store
views
App.vue
main.js
3、修改相应的文件的配置(首页,分类,购物车,我的,详情)
3.1 先写好各个页面(页面结构为上下结构,上包含头部和内容,下包含底部)
src/App.vue
-
首页
-
分类
-
购物车
-
我的
export default {
}
@import "~@/lib/reset.scss";
html, body, .container, .detail{
@include rect(100%, 100%); // width: 100%;height:100%;
}
.container, .detail{
@include flexbox(); // display:flex;display: box;.... 弹性盒布局的兼容写法
@include flex-direction(column); // flex-direction: column;
.box{
@include flex(); // flex: 1;
@include rect(100%, auto);
@include flexbox();
@include flex-direction(column);
.header {
@include rect(100%,0.44rem);
@include background-color(#f66);
}
.content {
@include flex();
@include rect(100%, auto);
@include overflow();
}
}
.footer {
@include rect(100%, 0.5rem);
@include background-color(#efefef);
ul {
@include rect(100%, 100%);
@include flexbox();
li {
@include text-color(#333);
@include flex();
@include rect(auto, 100%);
@include flexbox();
@include justify-content(); // jusitify-content:center
@include align-items(); // align-items:center
@include flex-direction(column);
span {
@include font-size(24px);
}
p {
@include font-size(12px);
}
&.active {
@include text-color(#f66);
}
}
}
}
}
3.2 创建各个页面(home/kind/cart/user),以首页为例
src/views/home/index.vue
export default {}
3.3 抽离App.vue文件中的结构,再创建一个components/Footer.vue
vue路由的命名视图(多视图路由) ---- 一个路由映射多个地方发生改变 ---- 如果没有底部则不传底部
3.4 配置路由 --- 页面都是通过路由映射出来的 --- components: {default: "",footer: ""}
假设分类页面不需要footer,那么就不传footer
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: "/",
redirect: '/home'
},
{
path: '/home',
name: 'home',
components: {
default: () => import('@/views/home'),
footer: () => import('@/components/Footer')
}
},
{
path: '/kind',
name: 'kind',
components: {
default: () => import('@/views/kind')
}
},
{
path: '/cart',
name: 'cart',
components: {
default: () => import('@/views/cart'),
footer: () => import('@/components/Footer')
}
},
{
path: '/user',
name: 'user',
components: {
default: () => import('@/views/user'),
footer: () => import('@/components/Footer')
}
}
]
})
3.5 配置路由的连接 --- components/Footer.vue
tag属性可以生成目标标签
首页
分类
购物车
我的
export default {
}
如何区分被选中的路由,給li添加如下样式
li.router-link-exact-active.router-link-active {
@include text-color(#f66);
}
4、数据请求以及状态管理器
4.1 数据请求 --- 封装接口
cnpm i axios -S
api/index.js
import axios from 'axios'
import { resolve } from 'dns';
const isDev = process.env.NODE_ENV === '"development"'
// cnpm run dev ---- isDev = true
// cnpm run build --- idDev = false
// 一般在开发环境下会有跨域问题,生产环境代码在一台服务器下,不存在
// 如果开发遇到跨域问题,需要使用反向代理
// 如果能够解决开发环境与生产环境数据请求的地址呢?
// /daxun 开发环境反向代理标识
// https://www.daxunxun.com 生产环境真实的服务器
const baseUrl = isDev ? '/api' : 'https://www.daxunxun.com'
const api = {
requestGet (url) {
return new Promise((resolve, reject) => {
axios.get(baseUrl + url)
.then(res => {
resolve(res.data)
})
})
},
requestPost (url, data) {
return new Promise((resolve, reject) => {
axios.post(baseUrl + url, data)
.then(res => {
resolve(res.data)
})
})
}
}
export default api
如果跨域,需要反向代理
config/index.js
proxyTable: {
'/daxun': {
target: 'https://www.daxunxun.com/',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},
4.2 配置状态管理器
cnpm i vuex -S
store/home.js
export default {
state: { // 初始化的数据
homebannerdata: [],
homeprolist: []
},
getters: { // 计算属性
},
actions: { // 异步操作
},
mutations: { // 唯一改变数据的地方
}
}
store/index.js,如果有多个页面,需要引入多次
import Vue from 'vue'
import Vuex from 'vuex'
import home from './home'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
home
}
})
export default store
src/main.js将状态管理器添加到vue的根实例
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '
})
5、添加UI库的配置
PC:
element-ui :http://element-cn.eleme.io/2.0/#/zh-CN/component/installation
iview:https://www.iviewui.com/docs/guide/install
移动:
mint-ui:https://mint-ui.github.io/
vant-ui:https://youzan.github.io/vant/#/zh-CN/intro
以mint-ui为例
cnpm i mint-ui -S
cnpm i babel-plugin-component -D
修改.babelrc文件如下:
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime",["component", [
{
"libraryName": "mint-ui",
"style": true
}
]]],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}
main.js入口文件处配置mint-ui
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import MintUI from 'mint-ui'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
Vue.use(MintUI)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '
})
6、搭建页面结构结合UI库
views/home/index.vue
import Vue from 'vue'
import { Swipe, SwipeItem } from 'mint-ui'
Vue.use(Swipe, SwipeItem)
export default {
}
.mint-swipe {
height: 160px;
border: 1px solid #ccc;
}
7、数据请求,结合状态管理,并且渲染至页面
/store/home.js
import api from '@/api'
export default {
state: { // 初始化的数据
homebannerdata: [],
homeprolist: []
},
getters: { // 计算属性
},
actions: { // 异步操作
gethomebannerdata(context) { // 异步请求数据,context为action中的默认参数,代表是上下文
api.requestGet('/banner') //异步请求数据
.then(data => {
console.log(data)
let arr = []
data.map((item) => {
arr.push('https://www.daxunxun.com' + item)
})
context.commit('changehomebannerdata', arr) // 显式提交mutation----唯一改变数据源的方式
})
},
gethomeprolist (context) {
api.requestGet('/douban')
.then(data => {
console.log(data)
context.commit('changehomeprolist', data)
})
}
},
mutations: { // 唯一改变数据的地方
changehomebannerdata(state, data) {
state.homebannerdata = data
},
changehomeprolist (state, data) {
state.homeprolist = data
}
}
}
views/home/index.vue
import Vue from 'vue'
import { Swipe, SwipeItem } from 'mint-ui'
import {mapState,mapActions} from 'vuex'
Vue.use(Swipe, SwipeItem)
export default {
computed: {
...mapState({ // 拿到状态管理器中的数据
homebannerdata: (state) => state.home.homebannerdata,
homeprolist: (state) => state.home.homeprolist
})
},
mounted () {
this.gethomebannerdata()
this.gethomeprolist()
},
methods: {
...mapActions([
'gethomebannerdata', // 将 `this.gethomebannerdata()` 映射为 `this.$store.dispatch('gethomebannerdata')`
'gethomeprolist'
])
}
}
.mint-swipe {
height: 160px;
border: 1px solid #ccc;
}
img {
width: 100%;
}
还可以有等价写法
import Vue from 'vue'
import { Swipe, SwipeItem } from 'mint-ui'
import { mapState } from 'vuex'
Vue.use(Swipe, SwipeItem)
export default {
computed: {
...mapState({
homebannerdata: (state) => state.home.homebannerdata,
homeprolist: (state) => state.home.homeprolist
})
},
mounted () {
this.$store.dispatch('gethomebannerdata')
this.$store.dispatch('gethomeprolist')
}
}
.mint-swipe {
height: 160px;
border: 1px solid #ccc;
}
img {
width: 100%;
}
8、列表的渲染以及页面的跳转
编写详情页面 views/detail/index.vue
详情头部
详情内容
详情底部
export default {
}
views/home/index.vue渲染列表
-
{{ item.title }}
import Vue from 'vue'
import { Swipe, SwipeItem } from 'mint-ui'
import { mapState } from 'vuex'
Vue.use(Swipe, SwipeItem)
export default {
computed: {
...mapState({
homebannerdata: (state) => state.home.homebannerdata,
homeprolist: (state) => state.home.homeprolist
})
},
mounted () {
this.$store.dispatch('gethomebannerdata')
this.$store.dispatch('gethomeprolist')
}
}
.mint-swipe {
height: 160px;
border: 1px solid #ccc;
}
img {
width: 100%;
}
路由跳转
修改路由
{
path: '/detail/:id',
name: 'detail',
components: {
default: () => import('@/views/detail')
}
}
声明式跳转
{{ item.title }}
import Vue from 'vue'
import { Swipe, SwipeItem } from 'mint-ui'
import { mapState } from 'vuex'
Vue.use(Swipe, SwipeItem)
export default {
computed: {
...mapState({
homebannerdata: (state) => state.home.homebannerdata,
homeprolist: (state) => state.home.homeprolist
})
},
mounted () {
this.$store.dispatch('gethomebannerdata')
this.$store.dispatch('gethomeprolist')
}
}
.mint-swipe {
height: 160px;
border: 1px solid #ccc;
}
img {
width: 100%;
}
编程式跳转
-
{{ item.title }}
import Vue from 'vue'
import { Swipe, SwipeItem } from 'mint-ui'
import { mapState } from 'vuex'
Vue.use(Swipe, SwipeItem)
export default {
computed: {
...mapState({
homebannerdata: (state) => state.home.homebannerdata,
homeprolist: (state) => state.home.homeprolist
})
},
mounted () {
this.$store.dispatch('gethomebannerdata')
this.$store.dispatch('gethomeprolist')
},
methods: {
goDetail (item) {
// this.$router.push('/detail/' + item.id)
this.$router.push({
name: 'detail',
params: {
id: item.id
}
})
}
}
}
.mint-swipe {
height: 160px;
border: 1px solid #ccc;
}
img {
width: 100%;
}
详情接收数据
详情头部
详情内容 {{ title }}
详情底部
import api from '@/api'
export default {
data () {
return {
title: ''
}
},
mounted () {
// console.log(this.$route.params.id)
const id =this.$route.params.id
api.requestGet('/detail?id=' + id)
.then(data => {
console.log(data)
this.title = data[0].title
})
}
}
9、实现下拉刷新与上拉加载
views/kind/index.vue
import Vue from 'vue'
import { Cell, Loadmore } from 'mint-ui'
import api from '@/api'
Vue.use(Cell, Loadmore)
export default {
data () {
return {
kindlist: [],
allLoaded: false,
pageCode: 1
}
},
mounted () {
api.requestGet('/douban')
.then(data => {
this.kindlist = data
})
},
methods: {
loadTop () {// 下拉刷新函数
api.requestGet('/douban')
.then(data => {
this.kindlist = data// 重置数据
this.pageCode = 1 // 页面初始化
this.allLoaded = false// 表示可以加载数据
this.$refs.loadmore.onTopLoaded()// 下拉刷新结束
})
},
loadBottom () {
api.requestGet('/douban?count=20&start=' + this.pageCode * 20)
.then(data => {
if (data.length === 0) {
this.allLoaded = true// 没有数据了
} else {
this.kindlist = [...this.kindlist, ...data]// 合并数据
this.pageCode += 1// 页面加1
}
this.$refs.loadmore.onBottomLoaded()// 上拉加载结束
})
}
}
}