今天为大家带来一个Vue+VantUi完成的一个一个移动端电商项目,供大家学习和参考.
源码已上传到码云上面https://gitee.com/jiuyueqi/vyx,
如果有需要源码或者接口文档的欢迎评论区留言,看到后会第一时间进行回复
官网地址:https://youzan.github.io/vant/#/zh-CN/quickstart
通过vscode打开项目,使用 ctrl+~
打开终端。我们通过yarn来安装vant:
npm add vant
VantUI支持全局引入与按需引入,我们选用按需引入。
按需引入又分为自动按需引入与手动按需引入,我们选用自动按需引入:
yarn add babel-plugin-import
安装完成后,打开 babel.config.js
写入:
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
}
怎样验证安装成功了呢?我们引用一个Button组件,看看效果就知道了。
Button组件的文档:https://youzan.github.io/vant/#/zh-CN/button#dai-ma-yan-shi
main.js
中:
import { Button } from 'vant';
Vue.use(Button);
Home.vue
中:
<van-button type="primary">主要按钮van-button>
运行 yarn serve
可以看到:
如此,我们成功了!
Path Intellisense
- 打开 settings.json
,添加: "path-intellisense.mappings": {
"@": "${workspaceRoot}/src"
}
package.json
所在同级目录下创建文件 jsconfig.json
:{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": [
"node_modules"
]
}
如果我们所有要用到的组件都直接写在 main.js
中,那这份入口文件会很冗余,不利于维护。所以我们在 src/components
下新建 vant.js
,写入:
import Vue from 'vue'
import { Button } from 'vant';
Vue.use(Button);
在 main.js
中:
import '@/components/vant.js'
如此,我们所有要引入组件的代码,就在 vant.js
中来完成。
有时候我们会开启多个服务器进行模拟,为了表面冲突,有时候我们需要更改8080端口,我们可以在根目录下新建 vue.config.js
中:
module.exports = {
devServer: {
port: 5000
}
}
重新运行 yarn serve
就可以在 http://localhost:5000
中访问项目了。
该模块的主要功能是封装axios请求,并进行拦截器的书写
拦截器:主要用在验证登陆,携带后端要求的请求头token等
import axios from "axios"
const instance = axios.create({
baseURL: "http://kumanxuan1.f3322.net:8001",
timeout: 5000
})
// 两种拦截器的区别
// 封装axios拦截器,拦截的是请求,一般用在需要在请求中添加请求信息(例如:请求头)
// 路由拦截:拦截的是页面,可以决定用户有没有权利访问某个页面
// 拦截器
instance.interceptors.request.use(config => {
// 什么时候执行的?? 发请求之前执行这个函数
// 可以判断用户有没有登录, 如果没有登录就直接return, 请求就不会执行
// console.log("config", config); // 本次请求的一些信息
let token = localStorage.getItem("token")
if (token) {
// 携带登录凭证发起请求
config.headers["X-Nideshop-Token"] = token
}
return config
}, err => {
return Promise.reject(err)
})
instance.interceptors.response.use(res => {
// 什么时候执行的?? 在接收到响应之前,在执行then之前
// console.log("res", res); // 服务器响应的一些信息
return res; // 返回的这个res 被then方法的res形参接收了
}, err => {
return Promise.reject(err)
})
export default instance
api.js 用来管理请求
Home.vue
中:
created(){
GetHomeLists()
.then(res=>{
if(res.errno == 0){
console.log(res.data) // 成功拿到所有首页数据
}
})
}
在实际开发过程中我们往往需要进行路由的拦截,主要是
token
,如在未登录之前我们不允许进行购物车页面的访问,不许许直接查看收藏商品等功能
// 前置路由守卫
router.beforeEach((to, from, next) => {
// 要去往的路由对象
// console.log("to", to);
// 从哪里来的路由
// console.log("from", from);
// next放行 可以顺利到达要去的路由
// 获取登录凭证
let token = localStorage.getItem("token");
if (to.path == "/cart") { // 表示去往购物车页面
// 判断有没有登录,判断token是否存在
if (token) {
next()
} else {
// 表示没有登录
Vue.prototype.$toast("请先登录")
setTimeout(() => {
next("/user") // 跳转到user路由
}, 1000);
}
// return
}
next()
})
我们对 vue.config.js
进行配置:
module.exports = {
devServer: {
port: 5000,
proxy: {
'/api': {
target: "http://kumanxuan1.f3322.net:8001/",
pathRewrite: {
'^/api': ''
}
}
}
}
}
由于配置文件修改了,这里一定要记得重新 npm run serve
!!
created() {
// 组件刚创建的时候需要判断用户是否登录,判断localStorage有没有token值
// 如果有就应该天上用户信息
let token = localStorage.getItem("token")
if (token) {
// 表示已经有登录状态,就要渲染用户信息
let userInfo = JSON.parse(localStorage.getItem('userInfo'));
this.nickname = userInfo.nickname;
this.avatarSrc = userInfo.avatar;
}
},
methods: {
onSubmit(values) {
// console.log('submit', values);
let username = values["用户名"];
let pwd = values["密码"];
GoLogin({
username,
pwd
}).then(res=> {
// console.log(res.data);
if(res.data.errno == 0){
console.log(res.data);
// 登录成功了
// 1.提示框提示登录成功
this.$toast.success('登录成功');
// 2.把token保存到本地存储
localStorage.setItem("token", res.data.data.token);
localStorage.setItem("userInfo", JSON.stringify(res.data.data.userInfo));
// 3.1s后关闭模态框
setTimeout(() => {
this.isShowModal = !this.isShowModal;
// 4.把拿到的userInfo填写到页面上
this.nickname = res.data.data.userInfo.nickname;
this.avatarSrc = res.data.data.userInfo.avatar;
}, 1000);
}
})
},
openModal() {
// 判断用户是否登录,如果登录就直接return
// 没有登录就取反
let token = localStorage.getItem("token")
if(token) {
return
}
this.isShowModal = !this.isShowModal
}
},
}
transition
就可以做一些过度效果,vue官就给我们提供了一些钩子函数
来让我们进行这一操作// Vue官网的方式
.slide-enter, .slide-leave-to {
// 过渡之前的样式写在这里
right: -100%;
}
.slide-enter-active,.slide-leave-active {
// 过渡属性写在这里
transition: all .3s;
}
.slide-enter-to, .slide-leave {
// 过渡之后的样式写在这里
right: 0;
}
// vant提供的方式
<transition name="van-slide-right">
<router-view>router-view>
transition>
安装vuex:
npm vuex
在 src 下新建 store/index.js
,用来控制弹出层是否显示:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
isShowPopupShow: false
},
mutations: {
changeIsShowPopupShow(state, payload) {
state.isShowPopupShow = payload
}
},
actions: {},
modules: {}
})
main.js
中:
...
import store from '@/store/index.js'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Home.vue
中:
<search @showpopup="$store.commit('togglePopup', true)">search>
<van-popup v-model="$store.state.showPopup" position="right" :style="{ height: '100%', width: '90%' }">
<sub-popup>sub-popup>
van-popup>
可以在 {commit}
之后追加 rootState
getGoodsList({ commit, rootState }) {
GetSearchGoodsList({
params: {
keyword: rootState.searchVal,
...
}
}).then(res=>{
...
})
先进性深拷贝,再做替换:
JSON.parse(JSON.stringify(data).replace(/name1/g, 'new_name1').replace(/name2/g, 'new_name2').replace(/name3/g, 'new_name3')...)
replace可以重复链式编程,name1表示旧属性名,new_name表示新属性名
如果你在开发中,碰到vantui的样式你无法替换,可以尝试在类名前面加 /deep/
:
section{
/deep/.box{
color: red;
}
}
理论上,修改state的值,唯一的途径是通过mutations。所以v-model直接使用state的值会出现问题,因此,我们可以借助computed属性来解决这个场面:
// store中:
const store = new Vuex.Store({
state: {
val: "你好,世界"
},
mutations: {
changeVal(state, payload){
state.val = payload;
}
}
})
// 组件中:
<input v-model="value" />
<script>
export default {
computed: {
value: {
get(){
return this.$store.state.val;
},
set(val){
this.$store.commit("changeVal", val);
}
}
}
}
</script>
在route目录下的index.js中加入以下代码即可解决
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function (location) {
return originalPush.call(this, location).catch(err => {})
}