vue+vant搭建移动端框架
主要内容
1.vant/rem移动端适配的解决方案
2. vue/cli二次配置及优化处理
3.基于注册动态模式实现loading
4.掌握vuex永久化存储
1 创建一个vue项目
1.1安装vue-cli:
npm install -g @vue/cli
1.2初始化项目
vue create vue-vant
(1)选择手动配置
空格切换选中状态
插件选择这边选择了(Babel、Router、Vuex、Css预处理器、Linter / Formatter 格式检查)
Babel:支持es6语法转换
TypeScript:微软提供的js超集
Progressive Web App (PWA) Support:渐进式的网页应用程序。
Router:路由
Vuex:Vuex是Vue.js应用程序的状态管理模式+库
CSS Pre-processors:Sass/Less预处理器
Linter / Formatter:代码风格检查
Unit Testing:支持单元测试
E2E Testing:支持E2E测试
(2)
路由模式选择: 是否使用history模式的路由(yes)
选择一个css预处理器(Less)
选择一个eslint配置,这边选择Airbnb
选择在什么时候进行eslint校验, 选择Lint on save保存时检查
选择将这些配置写入到什么地方( In package.json)
是否保存这份预设配置(n)
到此等待依赖完成
1.3启动项目
cd vue-vant
npm run serve
2.通过npm安装
npm I vant -S
参考vant官网快速上手教程: https://youzan.github.io/vant/#/zh-CN/quickstart
3.引入组件
自动按需引入(推荐)
3.1安装babel-plugin-import插件
babel-plugin-import 是一款 babel 插件,它会在编译过程中将 import 的写法自动转换为按需引入的方式
#安装插件
npm i babel-plugin-import -D
3.2添加配置
在babel.config.js 中添加配置
{
"plugins": [
["import", {
"libraryName": "vant",
"libraryDirectory": "es",
"style": true
}]
]
}
3.3引入vant组件
如在全局引入懒加载组件
src/main.js
//引入组件
import {Lazyload} from 'vant'
Vue.use(Lazyload)
引入组件后发现报错:是因为项目开启了eslint检查
解决办法: 在项目根目录新建vue.config.js文件,去除在保存的时候检查代码的功能, 配置完后记得重新npm run serve
启动。
vue.config.js
module.exports = {
lintOnSave: false //eslint-loader 是否在保存的时候检查
}
vue.config.js是vue-cli3之后新增的一个功能,再这个版本里面如果要配置webpack相关的属性,就需要自己在项目根目录新建vue.config.js这个文件,然后在该文件里面去写入你需要的配置。
后面再对vue.config.js进行更详细配置。
4.rem适配
Vant 中的样式默认使用px作为单位,如果需要使用rem单位,推荐使用以下两个工具:
postcss-pxtorem 是一款 postcss 插件,用于将单位转化为 rem
lib-flexible 用于设置 rem 基准值
4.1安装
npm install postcss-pxtorem lib-flexible --save-dev
4.2postcss配置
在项目目录下新建文件postcss.config.js
postcss.config.js
//postcss的配置
module.exports = {
plugins: {
autoprefixer: {
browsers: ['Android >= 4.0', 'iOS >= 8'],
},
'postcss-pxtorem': {
rootValue: 37.5,
propList: ['*'],
},
},
};
4.3引入 lib-flexible
main.js
//引入lib-flexible 用于设置rem基准值
import "lib-flexible/flexible"
rem详解
1rem等于html根元素设定的font-size的px值,vantui设置rootValue: 37.5,则1rem 等于 37.5px 等于 设备的宽度/10
切换不同的机型, 根元素有不同的font-size, 当写css px样式时, 会被程序换算成rem达到适配 使用vant组件,
需要按照rootValue:37.5来写样式
举个例子: 一张750px * 1334px图片, 在iphone6上铺满屏幕, 其他机型适配
当rootValue:75, 样式width:750px;height:1334px;图片会撑满iphone6屏幕,切换其他机型图片同样撑满屏幕
当rootValue:37.5, 样式width:375px;height:667px;图片会撑满iphone6屏幕
配置完如下图所示说明配置成功
<html data-dpr="1" style="font-size: 41.4px;"></html>
5.Tabbar标签栏的使用
5.1初始化项目
App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
Home.vue
<template>
<div class="home">
</div>
</template>
<script>
export default {
name: 'Home',
};
</script>
5.2引入
App.vue
<script>
import { Tabbar, TabbarItem } from 'vant';
export default {
components: {
[Tabbar.name]: Tabbar,
[TabbarItem.name]: TabbarItem,
},
}
</script>
5.3路由模式
标签栏支持路由模式,用于搭配vue-router使用。路由模式下会匹配页面路径和标签的to属性,并自动选中对应的标签
App.vue
<router-view/>
<van-tabbar route>
<van-tabbar-item replace to="/" icon="home-o">首页</van-tabbar-item>
<van-tabbar-item replace to="/about" icon="orders-o">介绍</van-tabbar-item>
</van-tabbar>
实现效果如下图
6.Swipe轮播的使用
6.1引入
Home.vue
<script>
import { Swipe, SwipeItem} from "vant";
export default {
name: 'Home',
components: {
[Swipe.name]: Swipe,
[SwipeItem.name]: SwipeItem
},
};
</script>
6.2图片懒加载
当 Swipe 中含有图片时,可以配合 Lazyload 组件实现图片懒加载。
Home.vue
<van-swipe :autoplay="3000">
<van-swipe-item v-for="(image, index) in images" :key="index">
<img v-lazy="image" />
</van-swipe-item>
</van-swipe>
import Vue from 'vue';
import { Lazyload } from 'vant';
Vue.use(Lazyload);
export default {
data() {
return {
images: [
'https://img.yzcdn.cn/vant/apple-1.jpg',
'https://img.yzcdn.cn/vant/apple-2.jpg',
],
};
},
};
6.3 设置图片样式
Home.vue
<template>
<div class="home">
<van-swipe :autoplay="3000">
<van-swipe-item v-for="(image, index) in images" :key="index">
<img v-lazy="image" />
</van-swipe-item>
</van-swipe>
</div>
</template>
<script>
import { Swipe, SwipeItem} from "vant";
export default {
name: 'Home',
data() {
return {
images: [
'https://img.yzcdn.cn/vant/apple-1.jpg',
'https://img.yzcdn.cn/vant/apple-2.jpg',
],
};
},
components: {
[Swipe.name]: Swipe,
[SwipeItem.name]: SwipeItem
},
};
</script>
<style scoped>
img{
width: 100%;
}
</style>
最终效果如下图:
7.配置多环境变量
7.1配置介绍
以 VUE_APP_ 开头的变量,在代码中可以通过 process.env.VUE_APP_ 访问。
比如,VUE_APP_BASE_API = ‘development’ 通过process.env.VUE_APP_BASE_API访问。
除了 VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量NODE_ENV 和BASE_URL。
7.2在项目根目录中新建.env, .env.development 和 .env.production, 在文件中定义变量
.env.development 本地开发环境配置
NODE_ENV = "development";
BASE_URL = "/";
VUE_APP_BASE_API ="/dev-api";
.env.production 正式环境配置
NODE_ENV = "production";
BASE_URL = "/";
VUE_APP_BASE_API ="/prod-api";
.env 公共环境配置
NODE_ENV = "development";
BASE_URL = "/";
VUE_APP_BASE_API ="/dev-api";
VUE_APP_AA="aa"
7.3 vue.config.js配置
vue.config.js的具体配置参数可以参照vue-cli文档地址:https://cli.vuejs.org/zh/config/#crossorigin
vue.config.js
// module.exports = {
// lintOnSave: false //eslint-loader 是否在保存的时候检查
// }
console.log(process.env.NODE_ENV);
const port = process.env.port || 9999;
module.exports = {
// 是否在开发环境下通过 eslint-loader 在每次保存时 lint 代码
// (在生产构建时禁用 eslint-loader) 应该是!==,这里设置为===是为了不进行检查
lintOnSave: process.env.NODE_ENV === 'production',
devServer:{
port,
open:true,
// 前端应用和后端 API 服务器没有运行在同一个主机上,
// 你需要在开发环境下将 API 请求代理到 API 服务器
proxy:{
[process.env.VUE_APP_BASE_API]:{
target:"http://localhost:8000/mock",// 接口的域名
changeOrigin:true,// 开启代理,在本地创建一个虚拟服务端
pathRewrite:{
[process.env.VUE_APP_BASE_API]:""
}
}
}
}
}
8.配置alias别名
vue.config.js
const path = require('path');
function resolve(dir){
return path.join(__dirname,dir);
}
module.exports = {
configureWebpack:{
resolve:{
alias:{
"@":resolve("src"),
"components":resolve("src/components"),
"utils":resolve("src/utils")
}
}
}
}
9.使用vuex实现按钮功能
9.1添加按钮
Home.vue
<template>
<div class="home">
<van-button type="primary" @click="addOne">按钮</van-button>
</div>
</template>
<script>
import { Button } from "vant";
export default {
name: 'Home',
components: {
[Button.name]: Button
},
methods: {
//添加按钮触发事件
addOne(){
console.log("点击了按钮")
}
}
};
</script>
9.2使用Vuex 实现点击按钮两秒钟后数字加1
store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
},
});
main.js引入
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
使用
store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
//初始状态, 存储基本数据
state: {
number: 1
},
//更改store中状态的方法
mutations: {
oneAsync(state, payload){
state.number = state.number + payload
}
},
//用于提交mutation
actions: {
oneAsync({commit}, payload){
return new Promise((resolve, reject) => {
window.setTimeout(() => {
resolve()
commit("oneAsync", payload)
}, 2000)
})
}
},
//允许将单一的store拆分为多个store
modules: {
},
});
Home.vue
<template>
<div class="home">
<h1>当前数据是:{{$store.state.number}}</h1>
<van-button type="primary" @click="addOne" :loading="loading">按钮</van-button>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
name: 'Home',
components: {
[Button.name]: Button
},
methods: {
//mapActions: 创建组件方法分发actions
// 将 `this.oneAsync()` 映射为 `this.$store.dispatch('oneAsync')`
// store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise
...mapActions(["oneAsync"]),
addOne(){
this.oneAsync(1)//加1
}
}
};
</script>
实现效果如下,点击按钮两秒后下图当前数据将变为2:
vuex的理解:
vuex文档: https://vuex.vuejs.org/zh/guide/
vuex是一个专门为vue.js开发的状态管理模式,每一个vuex应用核心就是store(仓库)。store基本上就是一个容器,它包含着你的应用中大部分的state(状态)
vuex的状态存储是响应式的,当 vue组件中store中读取状态时候,若store中的状态发生变化,那么相应的组件也会相应地得到高效更新。
改变store中的状态的唯一途径就是显示 commit(提交)mutation,这样使得我们可以方便地跟踪每一个状态的变化。
主要有以下几个模块:
State: 定义了应用状态的数据结构,可以在这里设置默认的初始状态
Getter: 允许组件从Stroe中获取数据,mapGetters辅助函数仅仅是将store中的 getter映射到计算属性。
Mutation: 唯一更改store中状态的方法,且必须是同步函数。
Action: 用于提交muatation, 而不是直接变更状态,可以包含任意异步操作。
Module: 允许将单一的store拆分为多个 sotre且同时保存在单一的状态树中
10自定义 vuex-plugins-loading
插件功能: https://vuex.vuejs.org/zh/guide/plugins.html
subscribeAction: https://vuex.vuejs.org/zh/api/#subscribeaction
实现效果: 页面在数据加载完成前展示loading
实现思路:vuex中注册一个管理loading的module,通过绑定异步的action,将每个action的loading存在vuex中,这样我在每个页面只需要在vuex的store中拿相对应的action loading就能达到此目的
在src目录下新建utils目录,在utils目录下再新建vuex-loading.js
utils/vuex-loading.js
const NAMESPACED = "myLoading";
const createLoadingPlugin = ({namespaced = NAMESPACED}={})=>{
return store =>{
if(store.state[namespaced]){
throw new Error("createLoadingPlugin is exited");
}
store.registerModule(namespaced, {
namespaced:true,
state:{
effect:{}
},
mutations:{
show(state,payload){
state.effect = {
...state.effect,
[payload]:true
}
},
hide(state,payload){
state.effect = {
...state.effect,
[payload]:false
}
}
}
})
// 核心代码
// 也可以指定订阅处理函数的被调用时机应该在一个 action 分发之前还是之后 (默认行为是之前)
// 订阅 store 的 action,
// handler 会在每个 action 分发的时候调用并接收 action 描述和当前的 store 的 state 这两个参数
store.subscribeAction({
// 调用action之后loading一直是true
before: (action, state) => {
console.log(`before action ${action.type}`)//before action oneAsync
store.commit(namespaced+"/show",action.type)
},
after: (action, state) => {
console.log(`after action ${action.type}`)//after action oneAsync
store.commit(namespaced+"/hide",action.type)
}
})
}
}
export default createLoadingPlugin;
引入并使用插件
store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
//引入插件
import createLoadingPlugin from '../utils/vuex-loading';
Vue.use(Vuex);
export default new Vuex.Store({
//使用插件
plugins: [createLoadingPlugin()],
});
Home.vue
<template>
<div class="home">
<!-- 在点击前后增加loading -->
<h1>当前数据是:{{$store.state.number}}</h1>
<van-button type="primary" @click="addOne" :loading="loading">按钮</van-button>
{{$store.state.myLoading.effect.oneAsync}}
</div>
</template>
<script>
import { Swipe, SwipeItem } from 'vant';
import { Button } from 'vant';
import { mapActions, mapState } from 'vuex';
export default {
name: 'Home',
computed: {
//拿到action中的loading
...mapState({loading:state=>state.myLoading.effect["oneAsync"]})
},
components: {
[Swipe.name]: Swipe,
[SwipeItem.name]: SwipeItem,
[Button.name]: Button
},
methods: {
//mapActions: 创建组件方法分发actions
// 将 `this.oneAsync()` 映射为 `this.$store.dispatch('oneAsync')`
// store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise
...mapActions(["oneAsync"]),
addOne(){
this.oneAsync(1)
}
}
};
</script>
11.vuex永久化存储
vuex 是 vue的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。
这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换。
store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
//引入插件
import createLoadingPlugin from '../utils/vuex-loading';
Vue.use(Vuex);
//vuex永久化存储
function vuexlocal(){
return store =>{
//看下本地是否保存数据
//监视 subscribe
// JSON.parse 将字符串转化为对象
// JSON.stringify 将 JavaScript 值转换为 JSON 字符串
let local = JSON.parse(localStorage.getItem("myVuex")) ||store.state;
store.replaceState(local);
store.subscribe((mutation,state)=>{
let newState = JSON.parse(JSON.stringify(state));
localStorage.setItem("myVuex",JSON.stringify(newState))
})
}
}
更多推荐
所有评论(0)