vue中引入mintui、vux重构简单的APP项目
最近在学习vue时也了解到一些常用的UI组件,有用于PC的和用于移动端的。用于PC的有:Element(饿了么)、iView等;用于移动端APP的有Vux、Mint UI(饿了么)、Vant(有赞团队)、cube-ui(滴滴)。
想做一个移动端微信公众号使用的界面,于是选择用mintUI。
mintUI官网:http://mint-ui.github.io/docs/#/en2/quickstart
1.项目中引入mint-ui
1.下载以及配置
(1)下载:
E:\HBuilderSpace\vue-demo>cnpm install --save mint-ui
(2)引入:可以按需引入,也可以全部引入
全部引入的方式:
main.js:
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import MintUI from 'mint-ui'
import 'mint-ui/lib/style.css'
Vue.config.productionTip = false;
Vue.use(MintUI)
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');
比如我们在home.vue使用头部固定的header:(只是简单的实验,需要的时候到官网查阅即可)
<template> <div class="home"> <mt-header title="multiple button"> <router-link to="/" slot="left"> <mt-button icon="back">back</mt-button> <mt-button @click="handleClose">close</mt-button> </router-link> <mt-button icon="more" slot="right"></mt-button> </mt-header> <ul class="footer"> <router-link class="icons" to="/home/user">个人中心</router-link> <router-link class="icons" to="/home/contact">通讯录</router-link> </ul> <router-view/> </div> </template> <script> export default { name: 'home', }; </script> <style scoped lang="scss"> li{ list-style: none; } .footer{ position: fixed; width: 100%; height: 60px; line-height:60px; left: 0px; bottom: 0px; display: flex; flex-flow: row nowrap; justify-content: space-around; } .icons{ font-size: 16px; flex: 1; text-align:center; border-top: 1px solid #42b983; } a { color: #42b983; &.active{ color: #fff; background:#42b983; } } </style>
最终效果如下:
按需引入的方式:(比如我们按需引入所需的按钮模块)
(1)main.js引入模块和css:
import Vue from 'vue'; import App from './App.vue'; import router from './router'; import store from './store'; import { Button } from 'mint-ui'; import 'mint-ui/lib/style.css' Vue.component(Button.name, Button); Vue.config.productionTip = false; new Vue({ router, store, render: h => h(App), }).$mount('#app');
(2).vue文件直接使用:
<mt-button size="large" type="primary">large</mt-button>
结果:
2. 重构之前的项目,改为mintUI
主要代码如下:
App.vue:只定义了入口,在router/index.js中设置默认路由
<template> <div id="app"> <router-view/> </div> </template> <style lang="scss"> *{ padding: 0px; text-align: center; } </style>
Main.js:按需引入所需的mint模块,并且用Vue.component(name, options)发布为全局组件
import Vue from 'vue'; import App from './App.vue'; import router from './router'; import store from './store'; import 'mint-ui/lib/style.css' import { Button } from 'mint-ui'; import { Field } from 'mint-ui'; import { Header } from 'mint-ui'; import { Navbar, TabItem } from 'mint-ui'; Vue.component(Navbar.name, Navbar); Vue.component(TabItem.name, TabItem); Vue.component(Header.name, Header); Vue.component(Field.name, Field); Vue.component(Button.name, Button); Vue.config.productionTip = false; new Vue({ router, store, render: h => h(App), }).$mount('#app');
router/index.js:
import Vue from 'vue'; import VueRouter from 'vue-router'; import Login from '../views/Login.vue'; Vue.use(VueRouter); const routes = [ { path: '/', name: 'login', component: Login, }, { path: '/home', name: 'home', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/Home.vue'), redirect: '/home/user', children: [{ path: 'user', name: 'user', component: () => import(/* webpackChunkName: "about" */ '../views/User.vue') }, { path: 'contact', name: 'contact', component: () => import(/* webpackChunkName: "about" */ '../views/Contact.vue') }] }, ]; const router = new VueRouter({ mode: 'hash', base: process.env.BASE_URL, linkActiveClass: 'active', routes, }); export default router;
定义了默认路由是Login,Login采用一次性加载,其他采用懒加载。登录成功之后的/home重定向到子路由/home/user。
store/index.js
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: { username: '' }, mutations: { setLoginUsername(state, username) { state.username = username }, }, actions: { setLoginUsernameFun(context, username) { context.commit("setLoginUsername", username); }, }, modules: { }, });
vuex管理组件定义了一个属性与修改属性的方法
views/Login.vue
<template> <div class="login"> <form v-if="!isReg"> <h1>欢迎来到XXX系统</h1> <br/> <mt-field label="用户名" placeholder="Input username" v-model="name"></mt-field> <mt-field label="密 码" placeholder="Input password" type="password" v-model="password"></mt-field> <mt-button size="large" type="primary" @click.prevent="login()" key="login">登录</mt-button> <br/> <mt-button size="large" type="default" @click.prevent="reg()" key="reg" plain>注册</mt-button> <br/> </form> <div v-else> <h1>注册</h1> <br/> <br/> <mt-field label="用户名" placeholder="Input username" v-model="name"></mt-field> <mt-field label="密 码" placeholder="Input password" type="password" v-model="password"></mt-field> <mt-field label="密 码" placeholder="Input passwordRepeat" type="password" v-model="passwordRepeat"></mt-field> <mt-button size="large" type="primary" @click="addUser()" key="doReg">注册</mt-button> <br/> <mt-button size="large" type="default" @click="cancel()" key="cancel" plain>取消</mt-button> </div> </div> </template> <script> import store from '@/store'; import { MessageBox } from 'mint-ui'; import { Toast } from 'mint-ui'; export default { name: 'login', store, data() { return { isReg: false, name: '', password: '', }; }, methods: { login() { this.isReg = false; var nameLocal = localStorage.getItem("name"); var passwordLocal = localStorage.getItem("password"); if (nameLocal == '' || passwordLocal == '') { MessageBox.alert("您还没注册!", "title"); return; } if (nameLocal === this.name && passwordLocal === this.password) { store.dispatch("setLoginUsernameFun", nameLocal); Toast("登录成功"); this.$router.push('/home') return false; } MessageBox.alert("账号密码错误"); }, reg() { this.name = ''; this.password = ''; this.passwordRepeat = ''; this.isReg = true; }, addUser() { if (this.name == '' || this.password == '') { MessageBox.alert("必填用户名密码!!!"); return; } if (this.password !== this.passwordRepeat) { MessageBox.alert("两次密码不一致!!!"); return; } localStorage.setItem("name", this.name); localStorage.setItem("password", this.password); this.name = ''; this.password = ''; this.isReg = false; Toast("注册成功"); }, cancel() { this.isReg = false; } }, }; </script>
定义了两个表单,登录和注册用的,用isReg属性进行切换。注册成功保存到本地localStorage。
注意:(1)mint中button默认类型是submit,所以需要阻止事件的默认行为。(2)mintUI的JS组件不可以在main.js中全局引入。比如MessageBox、Toast。
views/Home.vue
<template> <div class="home"> <mt-header title="XXX管理系统"> <router-link to="/" slot="left"> <mt-button icon="back">back</mt-button> </router-link> <mt-button icon="more" slot="right"></mt-button> </mt-header> <router-view/> <ul class="footer"> <router-link class="icons" to="/home/user">个人中心</router-link> <router-link class="icons" to="/home/contact">通讯录</router-link> </ul> </div> </template> <script> export default { name: 'home', }; </script> <style scoped lang="scss"> li { list-style: none; } .footer { position: fixed; width: 100%; height: 60px; line-height: 60px; left: 0px; bottom: 0px; display: flex; flex-flow: row nowrap; justify-content: space-around; } .icons { font-size: 16px; flex: 1; text-align: center; border-top: 1px solid #42b983; } a { color: #42b983; &.active { color: #fff; background: #42b983; } } </style>
定义了两个子路由,并用secc语法定义了样式。例如样式 a 里面的&.active 会被解析为 a.active,&代表当前选择器。 scoped 代表默认的css作用域是当前页面,如果不写会影响全局css样式,一般为当前页面。
views/Uer.vue
<template> <div class="user"> 个人中心。欢迎您: {{getLoginUsername()}} </div> </template> <script> import store from '@/store'; export default { name: 'User', store, methods: { getLoginUsername() { return store.state.username }, }, }; </script>
views/Contact.vue:
<template>
<div class="user">
通讯录
</div>
</template>
3.最终效果:
(1)登录:
(2)登录之后主页面:
4.引入axios实现前后端分离的注册、登录效果
1.安装axios
cnpm install --save axios
2.目录结构
3.重要代码解释
src/axios/index.js内容如下:
import axios from "axios"; import { MessageBox } from 'mint-ui'; // 引入常量模块 import Constants from '@/Constants.vue'; // 修改axios请求的默认配置(配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。) //` baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。 axios.defaults.baseURL = Constants.projectBaseAddress; // 添加请求拦截器 axios.interceptors.request.use(function(config) { // 模拟处理前增加token return config; }, function(error) { // 对请求错误做些什么 return Promise.reject(error); }); // 添加响应拦截器 axios.interceptors.response.use(function(response) { // 对响应数据做点什么 if(response.data.success) { // 如果是成功返回信息之后提取出来返回以供后面的调用链使用(后台返回的JSON数据) return response.data; } else { MessageBox.alert(response.data.msg); return new Promise(function(resolve, reject) { // resolve('success1'); // reject('error'); }); } }, function(error) { // 对响应错误做点什么 return Promise.reject(error); }); export default axios;
对axios进行改造之后重新发布对象。所有的请求前面增加一个baseURL,其值是Constants模块的projectBaseAddress。增加拦截器同一对后台返回false的结果进行弹窗,返回为true的提取返回的数据。
src/Constants.vue:
<script> export default { name: 'Constants', projectBaseAddress: '/api' }; </script>
作为一个全局JS工具类发布,使用的方式参考上面axios/index.js中使用。
vue.config.js中使用代理对所有/api/访问进行代理,解决跨域问题。当然跨域也可以后台设置允许跨域。这个参考express跨域设置规则就可以。
module.exports = { publicPath: './', lintOnSave: false, devServer: { proxy: { '/api': { target: 'http://localhost:8088', ws: true, changeOrigin: true, pathRewrite: { '^/api': '' } } } } }
Login.vue进行修改:登录注册采用同步的方式进行登录注册。async关键字+await。登录成功将username、fullname传回前台存入localStorage。
<template> <div class="login"> <form v-if="!isReg"> <h1>欢迎来到XXX系统</h1> <br/> <mt-field label="用户名" placeholder="Input username" v-model="username"></mt-field> <mt-field label="密 码" placeholder="Input password" type="password" v-model="password"></mt-field> <mt-button size="large" type="primary" @click.prevent="login()" key="login">登录</mt-button> <br/> <mt-button size="large" type="default" @click.prevent="reg()" key="reg" plain>注册</mt-button> <br/> </form> <div v-else> <h1>注册</h1> <mt-field label="用户名" placeholder="Input username" v-model="username"></mt-field> <mt-field label="用户姓名" placeholder="Input userfullname" v-model="userfullname"></mt-field> <mt-field label="密 码" placeholder="Input password" type="password" v-model="password"></mt-field> <mt-field label="电 话" placeholder="Input phone" v-model="phone"></mt-field> <mt-radio title="性 别" v-model="sex" :options="sexOptions" align="right"> </mt-radio> <mt-button size="large" type="primary" @click="addUser()" key="doReg">注册</mt-button> <br/> <mt-button size="large" type="default" @click="cancel()" key="cancel" plain>取消</mt-button> </div> </div> </template> <script> import store from '@/store'; import { MessageBox } from 'mint-ui'; import { Toast } from 'mint-ui'; import axios from "@/axios"; var login = { name: 'login', store, data() { return { isReg: false, username: '', userfullname: '', password: '', phone: '', sex: '', sexOptions: [{ label: '男性', value: '男' }, { label: '女性', value: '女' } ] }; }, methods: { async login() { this.isReg = false; if(this.username == "" || this.password == "") { MessageBox.alert("账号密码必须输入"); return; } var response = await axios.post('/doLoginJSON.html', { username: this.username, password: this.password }); if(response.success) { Toast("登录成功"); // 将用户存入localStorage localStorage.setItem("username", response.data.username); localStorage.setItem("userfullname", response.data.userfullname); // 跳转路由 this.$router.replace("/home"); } }, reg() { this.username = ''; this.password = ''; this.isReg = true; }, async addUser() { if(this.username == '' || this.password == '' || this.userfullname == '' || this.phone == '' || this.sex == '') { MessageBox.alert("请检查必填字段!!!"); return; } var response = await axios.post('/user/addUserJSON.html', { username: this.username, userfullname: this.userfullname, password: this.password, phone: this.phone, sex: this.sex, }); if(response.success) { this.isReg = false; Toast("注册成功"); } }, cancel() { this.isReg = false; } } } export default login; </script>
User.vue进行修改:(从localStorage获取当前登录的用户信息)
<template> <div class="user"> 个人中心。欢迎您: {{getLoginUsername()}} </div> </template> <script> import store from '@/store'; export default { name: 'User', store, methods: { getLoginUsername() { var username = localStorage.getItem("username"); var userfullname = localStorage.getItem("userfullname"); return userfullname; }, }, }; </script>
5.引入Vux
vux官网:https://vux.li/
查看了vux官网发现git上star的人数也不少,组件也满足自己的使用需求,于是选择了vux。而且vux提供了树形插件、图表统计插件以及常用的md5等工具函数。
1.简介
VUX(读音 [v’ju:z],同 views)是基于WeUI和Vue(2.x)开发的移动端UI组件库,主要服务于微信页面。
基于webpack + vue-loader + vux可以快速开发移动端页面,配合vux-loader方便你在WeUI的基础上定制需要的样式。
vux-loader保证了组件按需使用,因此不用担心最终打包了整个vux的组件库代码。
VUX并不完全依赖于WeUI,VUX 在 WeUI 的基础上扩展了多个常用组件,但是尽量保持整体UI样式接近WeUI的设计规范。
几个名词解释:
vue-loader:用于编译 .vue 文件,官方模板已经帮你配置好。
vux-loader:VUX 组件库的 webpack loader,实现按需加载等功能。它不是替代 vue-loader 而是配合 vue-loader 使用。如果你使用 vux2 模板,暂不需要手动使用它。
官网的建议
VUX 必须配合 vux-loader 使用,如果不使用 vux2 模板请按照文档正确配置。less@3.x 有严重的兼容问题,请暂时使用 less@^2.7.3。暂未适配 vue-cli@3.x。
2.安装
(1)安装vux
npm install vux --save
(2)安装less、less-loader (//安装less,vux使用的是less
)
npm install less less-loader --save-dev
(3)安装vux-loader:
vux是基于vux-loader的,所以必须要安装这个,否则会报一大堆错
npm install vux-loader --save-dev
(4)安装vue-loader:我没安装的时候报错,所以安装了该模块
npm install vue-loader@14.2.2 -D
(5)修改配置:vue.config.js:增加如下配置(vue-cli3.x版本的配置方式)
configureWebpack: config => { require('vux-loader').merge(config, { options: {}, plugins: ['vux-ui'] }) }
3.组件的使用:这里使用x-button组件
(1)安装组件,也可以称为注册组件:
局部注册:在模块中引入:
import { XButton } from 'vux' export default { components: { XButton } }
全局注册:在main.js中(有点类似于mintui的按需注册组件)
import { XButton } from 'vux' Vue.component('x-button', XButton)
(2)使用组件:
<x-button type="warn">vux按钮</x-button>
至此简单的引入了vux,接下来在使用的时候参考文档使用所需组件即可。
补充:关于vux的不一样的地方
(1)@click 绑定事件不生效。按照Vue文档,在组件上绑定原生点击事件请使用@click.native。
6.错误小记载
(1)npm run build的时候报错:
Invalid options object. CSS Loader has been initialised using an options object that does not match the API schema.
- options has an unknown property 'minimize'
在css-loader1.0版本的时候移除了该属性,我没有找到替代办法,所以我选择了降低css-loader版本解决:
npm install css-loader@0.28.11
总结:
0.@代表的是src目录
1.vue中引入全局JS变量和JS方法有两种:
(1)将全局的东西提取到单独的模块中,比如:抽到Constants.vue中,内容如下
<script> export default { name: 'Constants', projectBaseAddress: '/api', method1() { return "method1"; } }; </script>
其他模块使用的方法:引入模块,使用即可:
import Constants from '@/Constants.vue';
console.log(Constants.projectBaseAddress);
console.log(Constants.method1());
(2)通过Vue.prototype挂载到Vue实例中。可以挂载对象、函数、变量等,也可以是从模块中引入的对象。
var Constants = { name: 'Constants', projectBaseAddress: '/api', method1() { return "method1"; } }; Vue.prototype.Constants = Constants;
组件中调用即可:
console.log(this.Constants);
2.axios是基于Promise的ajax库,可以用async+await实现同步请求,其拦截器也可以做一些统一处理。
3.axios跨域请求可以在后台设置允许跨域+携带cookie,之后设置axios携带cookie。也可以使用代理,上面是使用了代理。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了