本文基于 vue-cli4.0+webpack 4+vant ui + sass+ rem 适配方案+axios 封装,构建手机端模板脚手架,开箱即用,让开发变得更简单。想看源代码请点击“阅读原文”,希望对你有所帮助!

Node 版本要求

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

本示例 Node.js 12.14.1

启动项目

git clone https://github.com/Detionl/vue-h5-template.gitcd vue-h5-templatenpm installnpm 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):

<html data-dpr="1" style="font-size: 37.5px;"></html>

切换不同的机型,根元素可能会有不同的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%,这里只是举例说明。

<img class="image" src="https://imgs.solui.cn/weapp/logo.png" /><style>/* rootValue: 75 */.image {width: 750px;height: 1334px;}/* rootValue: 37.5 */.image {width: 375px;height: 667px;}
</style>

✅ 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 文件之中

<style lang="scss">/* global styles */
</style><style lang="scss" scoped>/* local styles */
</style>

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-loadersprependData: `@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,.vue 文件中使用this.cdn,.vue文件中使用this.cdn访问

// 引入全局样式
import '@/assets/css/index.scss'// 设置 js中可以访问 $cdn
// 引入cdn
import {$cdn} from '@/config'
Vue.prototype.$cdn = $cdn

在 css 和 js 使用

<script>console.log(this.$cdn)
</script>
<style lang="scss" scoped>.logo {width: 120px;height: 120px;background: url($cdn+'/weapp/logo.png') center / contain no-repeat;}
</style>

✅ 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)
})

使用

<script>import {mapGetters} from 'vuex'export default {computed: {...mapGetters(['userName'])},methods: {// Action 通过 store.dispatch 方法触发doDispatch() {this.$store.dispatch('setUserName', '真乖,赶紧关注公众号,组织都在等你~')}}}
</script>

✅ 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 urlwithCredentials: true, // send cookies when cross-domain requeststimeout: 5000 // request timeout
})// request 拦截器 request interceptor
service.interceptors.request.use(config => {// 不传递默认开启loadingif (!config.hideloading) {// loadingToast.loading({forbidClick: true})}if (store.getters.token) {config.headers['X-Token'] = ''}return config},error => {// do something with request errorconsole.log(error) // for debugreturn Promise.reject(error)}
)
// respone拦截器
service.interceptors.response.use(response => {Toast.clear()const res = response.dataif (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 debugreturn 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 Router base 保持一直

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, // 是否启用websocketschangOrigin: 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').BundleAnalyzerPluginmodule.exports = {chainWebpack: config => {// 打包分析if (IS_PROD) {config.plugin('webpack-report').use(BundleAnalyzerPlugin, [{analyzerMode: 'static'}])}}
}
npm run build

✅ 配置 externals 引入 cdn 资源

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

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

因为页面每次遇到

暂时还没有研究放到自己的 cdn 服务器上。

const defaultSettings = require('./src/config/index.js')
const name = defaultSettings.title || 'vue mobile template'
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)// externals
const externals = {vue: 'Vue','vue-router': 'VueRouter',vuex: 'Vuex',vant: 'vant',axios: 'axios'
}
// CDN外链,会插入到index.html中
const cdn = {// 开发环境dev: {css: [],js: []},// 生产环境build: {css: ['https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.css'],js: ['https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js','https://cdn.jsdelivr.net/npm/vue-router@3.1.5/dist/vue-router.min.js','https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js','https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js','https://cdn.jsdelivr.net/npm/vant@2.4.7/lib/index.min.js']}
}
module.exports = {configureWebpack: config => {config.name = name// 为生产环境修改配置...if (IS_PROD) {// externalsconfig.externals = externals}},chainWebpack: config => {/*** 添加CDN参数到htmlWebpackPlugin配置中*/config.plugin('html').tap(args => {if (IS_PROD) {args[0].cdn = cdn.build} else {args[0].cdn = cdn.dev}return args})}
}

在 public/index.html 中添加

<% for (var i in
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>

<% } %> <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %> <% } %>

✅ 去掉 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移动端的脚手架相关推荐

  1. 手把手带你写一个JavaScript类型判断小工具

    业务写了很多,依然不是前端大神,我相信这是很多'入坑'前端开发同学的迷茫之处,个人觉得前端职业发展是有路径可寻的,前期写业务是一个积累过程,后期提炼总结,比如编程思想,父子类的原型继承,还是对象之间的 ...

  2. 【NLP】Pyhon+讯飞开放平台:​手把手带你写一个智能语音播报系统

    手把手带你写一个智能语音播报系统. 微信扫码登陆讯飞开放平台:https://www.xfyun.cn/ 完成个人认证. 在控制台创建应用,注意应用名称全库查重,很容易跟别人重复. 可查看到pytho ...

  3. 手把手带你撸一个校园APP(五):新闻中心模块

    这个项目是很早之前在学校做的,如今再回首.很多代码很是粗糙,逻辑也不尽完善.还望各位看官海涵. 前言 通过上一篇文章的功能设计,我们可以发现新闻通知公告等是APP的最主要功能点.主要是聚合展示学校官网 ...

  4. 手把手带你撸一个校园APP(六):失物招领二手交易模块

    代码经过简单的整理,已经放到Github上了.https://github.com/zhengweichao/Hevttc 回首来看,代码质量一般,里面也有各种逻辑问题,还望各位看官海涵.接下来有时间 ...

  5. Linux环境,手把手带你实现一个Nginx模块,深入了解Nginx丨惊群效应|error|负载均衡|Openresty丨C/C++Linux服务器开发丨中间件

    Linux环境,手把手带你实现一个Nginx模块,深入了解Nginx 视频讲解如下,点击观看: Linux环境,手把手带你实现一个Nginx模块,深入了解Nginx丨惊群效应|error|负载均衡|O ...

  6. 【技术分享篇】Linux内核——手把手带你实现一个Linux内核文件系统丨Linux内核源码分析

    手把手带你实现一个Linux内核文件系统 1. 内核文件系统架构分析 2. 行行珠玑,代码实现 [技术分享篇]Linux内核--手把手带你实现一个Linux内核文件系统丨Linux内核源码分析 更多L ...

  7. 如何封装一个vue组件(获取手机验证码的组件)

    如何封装一个vue组件(获取手机验证码的组件) 定义一个名为 item-time 的新组件,这是获取手机验证码的组件 Vue.component('item-time', {props: ['maxt ...

  8. 手把手带大家搭建一个java个人网站(腾讯云为例)

    大家好,我是鸟哥.一个半路出家的程序员. 这次真是学妹要的!前几天鸟哥以腾讯云为例给大家分享了一篇如何搭建服务器的文章--手把手带大家搭建一台服务器(腾讯云为例),文章结尾表示过几天带大家搭建一个网站 ...

  9. 手把手带你写一个 Vue3 的自定义指令

    背景 众所周知,Vue.js 的核心思想是数据驱动 + 组件化,通常我们开发页面的过程就是在编写一些组件,并且通过修改数据的方式来驱动组件的重新渲染.在这个过程中,我们不需要去手动操作 DOM. 然而 ...

最新文章

  1. Java 二分法查找
  2. ios打包ipa的四种实用方法
  3. spring boot 整合web开发(二)
  4. datastage 作业查找脚本
  5. 【数据结构】广义表的默认成员函数、深度、大小、打印
  6. Atitit nlp 自然语言处理的艺术 attilax著作 v2 t55.docx Atitit nlp 自然语言处理attilax总结 目录 1.1. 主要范畴 1 1.2. 研究难点
  7. php旧物交易开源代码_php二手市场交易系统毕业设计(含源文件)
  8. Android之SQlite存储
  9. 云平台与操作系统两种架构的分析
  10. 如何将两张图片合成一张图片
  11. 关于vlc编解码器暂不支持: VLC 无法解码格式“MIDI” (MIDI Audio)解决
  12. win10 休眠不读u盘_win10电脑不读u盘如何修复
  13. 丁火生于未月命理分析_丁火生于未月的性格特征
  14. Oracle数据库查询有多少表格,Oracle数据库的表有多大呢?
  15. redis 失效时间单位是秒还是毫秒_经常用Redis,这些坑你知道吗?
  16. SpringBoot redis GEO 实战应用
  17. 手动操作导航控制器的子视图控制器的数组
  18. 2016猴年春联集锦
  19. blockchain-wallet-sdk中的助记词部分使用示例
  20. English Learning - L3 综合练习 1 VOA-Color 2023.04.26 周三

热门文章

  1. mySQL首行缩进快捷键_html首行缩进、悬停效果
  2. 解决Error resolving template [x] template might not exist or might not be accessible by any of the con
  3. IOS移动APP网络层设计方案
  4. 第7节 利用win7漏洞绕过系统登录密码
  5. 会计初级考试重点知识梳理
  6. ssh 登录Petalinux
  7. 【CentOS7】U盘装 CentOS7 进入 dracut 真正解决办法
  8. Android 实现书籍翻页效果----升级篇
  9. Ubuntu 18.04 软件源修改成国内源(文件或界面形式)
  10. php诞生,PHP的发展历程