当前位置:首页 > 技术分析 > 正文内容

开箱即用 vue全家桶+vant移动端解决方案

ruisui883个月前 (02-03)技术分析7

作者:花花小仙女

转发链接:
https://mp.weixin.qq.com/s/c9uAYkWJu-zvKfELh_3V0A

前言

基于 vue-cli4.0+webpack 4+vant ui + sass+ rem 适配方案+axios 封装,构建手机端模板脚手架,开箱即用,让开发变得更简单。

先夸一下自己,收到了很多人的好评。这次好好整理一文,你们的鼓励就是我前进的动力。

github:https://github.com/sunniejs/vue-h5-template

Node 版本要求

Vue CLI它需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。你可以使用 nvm 或nvm-windows 在同一台电脑中管理多个 Node 版本。

本示例 Node.js 12.14.1

启动项目

git?clone?https://github.com/sunniejs/vue-h5-template.git

cd?vue-h5-template

npm?install

npm?run?serve

目录

  • √ Vue-cli4
  • √ 配置多环境变量
  • √ rem 适配方案
  • √ VantUI 组件按需加载
  • √ Sass 全局样式
  • √ Vuex 状态管理
  • √ Axios 封装及接口管理
  • √ Vue-router
  • √ Webpack 4 vue.config.js 基础配置
  • √ 配置 proxy 跨域
  • √ 配置 alias 别名
  • √ 配置 打包分析
  • √ 配置 externals 引入 cdn 资源
  • √ 去掉 console.log
  • √ splitChunks 单独打包第三方模块
  • √ 添加 IE 兼容
  • √ Eslint+Pettier 统一开发规范

? 配置多环境变量

package.json 里的 scripts 配置 serve stage build,通过 --mode xxx 来执行不同环境

  • 通过 npm run serve 启动本地 , 执行 development
  • 通过 npm run stage 打包测试 , 执行 staging
  • 通过 npm run build 打包正式 , 执行 production
"scripts":?{
??"serve":?"vue-cli-service?serve?--open",
??"stage":?"vue-cli-service?build?--mode?staging",
??"build":?"vue-cli-service?build",
}

配置介绍

??以 VUE_APP_ 开头的变量,在代码中可以通过 process.env.VUE_APP_ 访问。??比如,VUE_APP_ENV = 'development' 通过process.env.VUE_APP_ENV 访问。??除了 VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量NODE_ENV 和BASE_URL

在项目根目录中新建.env.*

  • .env.development 本地开发环境配置
NODE_ENV='development'
#?must?start?with?VUE_APP_
VUE_APP_ENV?=?'development'
  • .env.staging 测试环境配置
NODE_ENV='production'
#?must?start?with?VUE_APP_
VUE_APP_ENV?=?'staging'
  • .env.production 正式环境配置
?NODE_ENV='production'
#?must?start?with?VUE_APP_
VUE_APP_ENV?=?'production'

这里我们并没有定义很多变量,只定义了基础的 VUE_APP_ENV development staging production变量我们统一在 src/config/env.*.js 里进行管理。

这里有个问题,既然这里有了根据不同环境设置变量的文件,为什么还要去 config 下新建三个对应的文件呢?修改起来方便,不需 要重启项目,符合开发习惯。

config/index.js

//?根据环境引入不同配置?process.env.NODE_ENV
const?config?=?require('./env.'?+?process.env.VUE_APP_ENV)
module.exports?=?config

配置对应环境的变量,拿本地环境文件 env.development.js 举例,用户可以根据需求修改

//?本地环境配置
module.exports?=?{
??title:?'vue-h5-template',
??baseUrl:?'http://localhost:9018',?//?项目地址
??baseApi:?'https://test.xxx.com/api',?//?本地api请求地址
??APPID:?'xxx',
??APPSECRET:?'xxx'
}

根据环境不同,变量就会不同了

//?根据环境不同引入不同baseApi地址
import?{baseApi}?from?'@/config'
console.log(baseApi)

? rem 适配方案

不用担心,项目已经配置好了 rem 适配, 下免仅做介绍:

Vant 中的样式默认使用px作为单位,如果需要使用rem单位,推荐使用以下两个工具:

  • postcss-pxtorem 是一款 postcss 插件,用于将单位转化为 rem
  • lib-flexible 用于设置 rem 基准值

PostCSS 配置

下面提供了一份基本的 postcss 配置,可以在此配置的基础上根据项目需求进行修改

//?https://github.com/michael-ciniawsky/postcss-load-config
module.exports?=?{
??plugins:?{
????autoprefixer:?{
??????overrideBrowserslist:?['Android?4.1',?'iOS?7.1',?'Chrome?>?31',?'ff?>?31',?'ie?>=?8']
????},
????'postcss-pxtorem':?{
??????rootValue:?37.5,
??????propList:?['*']
????}
??}
}

更多详细信息:vant

新手必看,老鸟跳过

很多小伙伴会问我,适配的问题。

我们知道 1rem 等于html 根元素设定的 font-size 的 px 值。Vant UI 设置 rootValue: 37.5,你可以看到在 iPhone 6 下 看到 (1rem 等于 37.5px):

切换不同的机型,根元素可能会有不同的font-size。当你写 css px 样式时,会被程序换算成 rem 达到适配。

因为我们用了 Vant 的组件,需要按照 rootValue: 37.5 来写样式。

举个例子:设计给了你一张 750px * 1334px 图片,在 iPhone6 上铺满屏幕,其他机型适配。

  • 当rootValue: 70 , 样式 width: 750px;height: 1334px; 图片会撑满 iPhone6 屏幕,这个时候切换其他机型,图片也会跟着撑 满。
  • 当rootValue: 37.5 的时候,样式 width: 375px;height: 667px; 图片会撑满 iPhone6 屏幕。

也就是 iphone 6 下 375px 宽度写 CSS。其他的你就可以根据你设计图,去写对应的样式就可以了。

当然,想要撑满屏幕你可以使用 100%,这里只是举例说明。



? VantUI 组件按需加载

项目采 用 Vant 自动按需引入组件 (推荐) 下 面安装插件介绍:

babel-plugin-import 是一款 babel 插件,它会在编译过程中将import 的写法自动转换为按需引入的方式

安装插件

npm?i?babel-plugin-import?-D

在babel.config.js 设置

//?对于使用?babel7?的用户,可以在?babel.config.js?中配置
const?plugins?=?[
??[
????'import',
????{
??????libraryName:?'vant',
??????libraryDirectory:?'es',
??????style:?true
????},
????'vant'
??]
]
module.exports?=?{
??presets:?[['@vue/cli-plugin-babel/preset',?{useBuiltIns:?'usage',?corejs:?3}]],
??plugins
}

使用组件

项目在 src/plugins/vant.js 下统一管理组件,用哪个引入哪个,无需在页面里重复引用

//?按需全局引入?vant组件
import?Vue?from?'vue'
import?{Button,?List,?Cell,?Tabbar,?TabbarItem}?from?'vant'
Vue.use(Button)
Vue.use(Cell)
Vue.use(List)
Vue.use(Tabbar).use(TabbarItem)

? Sass 全局样式

首先 你可能会遇到 node-sass 安装不成功,别放弃多试几次!!!

目录结构,在 src/assets/css/文件夹下包含了三个文件

├──?assets
│???├──?css
│???│???├──?index.scss???????????????#?全局通用样式
│???│???├──?mixin.scss???????????????#?全局mixin
│???│???└──?variables.scss???????????#?全局变量

每个页面自己对应的样式都写在自己的 .vue 文件之中

??/*?global?styles?*/

??/*?local?styles?*/

vue.config.js 配置注入 sass 的 mixin variables 到全局,不需要手动引入 ,配置$cdn通过变量形式引入 cdn 地址

const?IS_PROD?=?['production',?'prod'].includes(process.env.NODE_ENV)
const?defaultSettings?=?require('./src/config/index.js')
module.exports?=?{
??css:?{
????extract:?IS_PROD,
????sourceMap:?false,
????loaderOptions:?{
??????scss:?{
????????//?注入?`sass`?的?`mixin`?`variables`?到全局,?$cdn可以配置图片cdn
????????//?详情:?https://cli.vuejs.org/guide/css.html#passing-options-to-pre-processor-loaders
????????prependData:?`
??????????@import?"assets/css/mixin.scss";
??????????@import?"assets/css/variables.scss";
??????????$cdn:?"${defaultSettings.$cdn}";
??????????`
??????}
????}
??}
}

在 main.js 中引用全局样式(发现在上面的,prependData 里设置@import "assets/css/index.scss";并没有应用全局样式这里在 main.js 引入)

设置 js 中可以访问 $cdn,.vue 文件中使用this.$cdn访问

//?引入全局样式
import?'@/assets/css/index.scss'

//?设置?js中可以访问?$cdn
//?引入cdn
import?{$cdn}?from?'@/config'
Vue.prototype.$cdn?=?$cdn

在 css 和 js 使用


??.logo?{
????width:?120px;
????height:?120px;
????background:?url($cdn+'/weapp/logo.png')?center?/?contain?no-repeat;
??}

? Vuex 状态管理

目录结构

├──?store
│???├──?modules
│???│???└──?app.js
│???├──?index.js
│???├──?getters.js

main.js 引入

import?Vue?from?'vue'
import?App?from?'./App.vue'
import?store?from?'./store'
new?Vue({
??el:?'#app',
??router,
??store,
??render:?h?=>?h(App)
})

使用

? Vue-router

本案例采用 hash 模式,开发者根据需求修改 mode base

注意:如果你使用了 history 模式,vue.config.js 中的 publicPath 要做对应的修改

import?Vue?from?'vue'
import?Router?from?'vue-router'

Vue.use(Router)
export?const?router?=?[
??{
????path:?'/',
????name:?'index',
????component:?()?=>?import('@/views/home/index'),?//?路由懒加载
????meta:?{
??????title:?'首页',?//?页面标题
??????keepAlive:?false?//?keep-alive?标识
????}
??}
]
const?createRouter?=?()?=>
??new?Router({
????//?mode:?'history',?//?如果你是?history模式?需要配置?vue.config.js?publicPath
????//?base:?'/app/',
????scrollBehavior:?()?=>?({y:?0}),
????routes:?router
??})

export?default?createRouter()

更多:Vue Router

? Axios 封装及接口管理

utils/request.js 封装 axios ,开发者需要根据后台接口做修改。

  • service.interceptors.request.use 里可以设置请求头,比如设置 token
  • config.hideloading 是在 api 文件夹下的接口参数里设置,下文会讲
  • service.interceptors.response.use 里可以对接口返回数据处理,比如 401 删除本地信息,重新登录
import?axios?from?'axios'
import?store?from?'@/store'
import?{Toast}?from?'vant'
//?根据环境不同引入不同api地址
import?{baseApi}?from?'@/config'
//?create?an?axios?instance
const?service?=?axios.create({
??baseURL:?baseApi,?//?url?=?base?api?url?+?request?url
??withCredentials:?true,?//?send?cookies?when?cross-domain?requests
??timeout:?5000?//?request?timeout
})

//?request?拦截器?request?interceptor
service.interceptors.request.use(
??config?=>?{
????//?不传递默认开启loading
????if?(!config.hideloading)?{
??????//?loading
??????Toast.loading({
????????forbidClick:?true
??????})
????}
????if?(store.getters.token)?{
??????config.headers['X-Token']?=?''
????}
????return?config
??},
??error?=>?{
????//?do?something?with?request?error
????console.log(error)?//?for?debug
????return?Promise.reject(error)
??}
)
//?respone拦截器
service.interceptors.response.use(
??response?=>?{
????Toast.clear()
????const?res?=?response.data
????if?(res.status?&&?res.status?!==?200)?{
??????//?登录超时,重新登录
??????if?(res.status?===?401)?{
????????store.dispatch('FedLogOut').then(()?=>?{
??????????location.reload()
????????})
??????}
??????return?Promise.reject(res?||?'error')
????}?else?{
??????return?Promise.resolve(res)
????}
??},
??error?=>?{
????Toast.clear()
????console.log('err'?+?error)?//?for?debug
????return?Promise.reject(error)
??}
)
export?default?service

接口管理

在src/api 文件夹下统一管理接口

  • 你可以建立多个模块对接接口, 比如 home.js 里是首页的接口这里讲解user.js
  • url 接口地址,请求的时候会拼接上 config 下的 baseApi
  • method 请求方法
  • data 请求参数 qs.stringify(params) 是对数据系列化操作
  • hideloading 默认 false,设置为 true 后,不显示 loading ui 交互中有些接口不需要让用户感知
import?qs?from?'qs'
//?axios
import?request?from?'@/utils/request'
//user?api

//?用户信息
export?function?getUserInfo(params)?{
??return?request({
????url:?'/user/userinfo',
????method:?'get',
????data:?qs.stringify(params),
????hideloading:?true?//?隐藏?loading?组件
??})
}

如何调用

//?请求接口
import?{getUserInfo}?from?'@/api/user.js'

const?params?=?{user:?'sunnie'}
getUserInfo(params)
??.then(()?=>?{})
??.catch(()?=>?{})

? Webpack 4 vue.config.js 基础配置

如果你的 Vue Router 模式是 hash

publicPath:?'./',

如果你的 Vue Router 模式是 history 这里的 publicPath 和你的 Vue Routerbase 保持一直

publicPath:?'/app/',
const?IS_PROD?=?['production',?'prod'].includes(process.env.NODE_ENV)

module.exports?=?{
??publicPath:?'./',?//?署应用包时的基本 URL。vue-router hash 模式使用
??//? publicPath:?'/app/', //?署应用包时的基本 URL。? vue-router history模式使用
??outputDir:?'dist',?//??生产环境构建文件的目录
??assetsDir:?'static',?//??outputDir的静态资源(js、css、img、fonts)目录
??lintOnSave:?false,
??productionSourceMap:?false,?//?如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
??devServer:?{
????port:?9020,?//?端口号
????open:?false,?//?启动后打开浏览器
????overlay:?{
??????//??当出现编译器错误或警告时,在浏览器中显示全屏覆盖层
??????warnings:?false,
??????errors:?true
????}
????//?...
??}
}

? 配置 proxy 跨域

如果你的项目需要跨域设置,你需要打开 vue.config.js proxy 注释 并且配置相应参数

注意:你还需要将
src/config/env.development.js 里的 baseApi 设置成 '/'

module.exports?=?{
??devServer:?{
????//?....
????proxy:?{
??????//配置跨域
??????'/api':?{
????????target:?'https://test.xxx.com',?//?接口的域名
????????//?ws:?true,?//?是否启用websockets
????????changOrigin:?true,?//?开启代理,在本地创建一个虚拟服务端
????????pathRewrite:?{
??????????'^/api':?'/'
????????}
??????}
????}
??}
}

使用 例如: src/api/home.js

export?function?getUserInfo(params)?{
??return?request({
????url:?'/api/userinfo',
????method:?'get',
????data:?qs.stringify(params)
??})
}

? 配置 alias 别名

const?path?=?require('path')
const?resolve?=?dir?=>?path.join(__dirname,?dir)
const?IS_PROD?=?['production',?'prod'].includes(process.env.NODE_ENV)

module.exports?=?{
??chainWebpack:?config?=>?{
????//?添加别名
????config.resolve.alias
??????.set('@',?resolve('src'))
??????.set('assets',?resolve('src/assets'))
??????.set('api',?resolve('src/api'))
??????.set('views',?resolve('src/views'))
??????.set('components',?resolve('src/components'))
??}
}

? 配置 打包分析

const?BundleAnalyzerPlugin?=?require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports?=?{
??chainWebpack:?config?=>?{
????//?打包分析
????if?(IS_PROD)?{
??????config.plugin('webpack-report').use(BundleAnalyzerPlugin,?[
????????{
??????????analyzerMode:?'static'
????????}
??????])
????}
??}
}
npm?run?build

? 配置 externals 引入 cdn 资源

这个版本 CDN 不再引入,我测试了一下使用引入 CDN 和不使用,不使用会比使用时间少。网上不少文章测试 CDN 速度快,这个开发者可 以实际测试一下。

另外项目中使用的是公共 CDN 不稳定,域名解析也是需要时间的(如果你要使用请尽量使用同一个域名)

因为页面每次遇到 ????<%?}?%>

? 去掉 console.log

保留了测试环境和本地环境的 console.log

npm?i?-D?babel-plugin-transform-remove-console

在 babel.config.js 中配置

//?获取?VUE_APP_ENV?非?NODE_ENV,测试环境依然?console
const?IS_PROD?=?['production',?'prod'].includes(process.env.VUE_APP_ENV)
const?plugins?=?[
??[
????'import',
????{
??????libraryName:?'vant',
??????libraryDirectory:?'es',
??????style:?true
????},
????'vant'
??]
]
//?去除?console.log
if?(IS_PROD)?{
??plugins.push('transform-remove-console')
}

module.exports?=?{
??presets:?[['@vue/cli-plugin-babel/preset',?{useBuiltIns:?'entry'}]],
??plugins
}

? splitChunks 单独打包第三方模块

module.exports?=?{
??chainWebpack:?config?=>?{
????config.when(IS_PROD,?config?=>?{
??????config
????????.plugin('ScriptExtHtmlWebpackPlugin')
????????.after('html')
????????.use('script-ext-html-webpack-plugin',?[
??????????{
????????????//?将?runtime?作为内联引入不单独存在
????????????inline:?/runtime\..*\.js$/
??????????}
????????])
????????.end()
??????config.optimization.splitChunks({
????????chunks:?'all',
????????cacheGroups:?{
??????????//?cacheGroups?下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块
??????????commons:?{
????????????name:?'chunk-commons',
????????????test:?resolve('src/components'),
????????????minChunks:?3,?//??被至少用三次以上打包分离
????????????priority:?5,?//?优先级
????????????reuseExistingChunk:?true?//?表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
??????????},
??????????node_vendors:?{
????????????name:?'chunk-libs',
????????????chunks:?'initial',?//?只打包初始时依赖的第三方
????????????test:?/[\\/]node_modules[\\/]/,
????????????priority:?10
??????????},
??????????vantUI:?{
????????????name:?'chunk-vantUI',?//?单独将?vantUI?拆包
????????????priority:?20,?//?数字大权重到,满足多个?cacheGroups?的条件时候分到权重高的
????????????test:?/[\\/]node_modules[\\/]_?vant(.*)/
??????????}
????????}
??????})
??????config.optimization.runtimeChunk('single')
????})
??}
}

? 添加 IE 兼容

之前的方式 会报 @babel/polyfill is deprecated. Please, use required parts of core-js
andregenerator-runtime/runtime separately

@babel/polyfill 废弃,使用 core-js 和 regenerator-runtime

npm?i?--save?core-js?regenerator-runtime

在 main.js 中添加

//?兼容?IE
//?https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md#babelpolyfill
import?'core-js/stable'
import?'regenerator-runtime/runtime'

配置 babel.config.js

const?plugins?=?[]

module.exports?=?{
??presets:?[['@vue/cli-plugin-babel/preset',?{useBuiltIns:?'usage',?corejs:?3}]],
??plugins
}

? Eslint+Pettier 统一开发规范

该文件 .prettierrc 里写 属于你的 pettier 规则

{
???"printWidth":?120,
???"tabWidth":?2,
???"singleQuote":?true,
???"trailingComma":?"none",
???"semi":?false,
???"wrap_line_length":?120,
???"wrap_attributes":?"auto",
???"proseWrap":?"always",
???"arrowParens":?"avoid",
???"bracketSpacing":?false,
???"jsxBracketSameLine":?true,
???"useTabs":?false,
???"overrides":?[{
???????"files":?".prettierrc",
???????"options":?{
???????????"parser":?"json"
???????}
???}]
}

鸣谢

vue-cli4-config: https://github.com/staven630/vue-cli4-config

vue-element-admin:https://github.com/PanJiaChen/vue-element-admin

mdnice:https://mdnice.com

作者:花花小仙女

转发链接:
https://mp.weixin.qq.com/s/c9uAYkWJu-zvKfELh_3V0A

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/776.html

标签: vue url传参
分享给朋友:

“开箱即用 vue全家桶+vant移动端解决方案” 的相关文章

Vue组件通信之props深入详解!

props 是 Vue 组件中一个很重要的概念。它是用来从父组件向子组件传递数据的。为什么需要props?这是因为在Vue中,组件是相互隔离的。每个组件都有自己的作用域,子组件无法直接访问父组件的状态或值。通过props,父组件可以将数据传递给子组件。使用props的步骤:1. 在子组件中定义pro...

java调用API操作GitLab

最近需要在一个WEB项目中集成GitLab,用到了GitLab的API操作,在网上找了很久都是说直接调用GitLab的Http接口,而且API官方只有javadoc没有其它说明文档,特别记录下,以备查询。这里采用Token的认证方式,因此需要先登陆GitLab新建一个Token,创建方式如下:创建完...

软件测试-性能测试专题方法与经验总结

本文 从 性能测试流程,性能测试指标,性能监测工具,性能测试工具,性能测试基线,性能测试策略,性能瓶颈分析方法几个维度,进行知识总结和经验分享;详细见下图总结,欢迎大家补充;性能测试经验与思考1. 性能测试流程1.1. 性格规格评审1.2. 资源排期1.2.1. 人力资源1.2.2. 时间计划· 性...

JavaScript数组操作:掌握常用方法,提升开发效率

JavaScript数组操作:从增删改查到高级应用本文深入解析JavaScript中常用的数组方法,包括push、unshift、pop、shift、map、filter、reverse、at 和 slice。通过详细的例子和应用场景,帮助开发者快速掌握这些方法,提升代码效率和可读性。开篇点题作为J...

Vue学习笔记之动态路由的参数传递应用及技巧

路由的参数传递:①通过params的类型· 配置路由格式:/router/:id· 传递的方式:在path后面跟上对应的值· 传递后形成的路径:/router/list,/router/profile这个就是前两篇中提到的"动态路由"中有应用过这个方法:②通过query的类型(对象方...

Vue实战篇|使用路由管理用户权限(动态路由)

权限控制是后台管理系统比较常见的需求,如果我们需要对某些页面的添加权限控制的话,那我们可以在路由管理中的权限做一些校验,没有通过权限校验的给出相应的提示或者直接跳转到报错页面。跟着我一起来学vue实战篇路由管理权限吧!权限校验函数getCurrentAuthority()函数用于获取当前用户权限,一...