VUX使用
作者:高天阳
邮箱:[email protected]
更改历史
* 2018-10-29 高天阳 整理文档、补充最佳实践
* 2018-10-22 高天阳 补充安装与使用、最佳实践
* 2018-10-15 高天阳 初始化文档
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
的设计规范。
VUX 并不是一个能解决所有场景的完美解决方案(实际上也没有一个方案能解决所有问题),也会出现某些
bug
或者某些特性不支持, 所以如果遇到问题麻烦及时不带情绪正确反馈,我们乐于及时解决描述详细方便重现的问题。即使你不直接使用
VUX
组件代码, 你依然可以参考VUX
代码来实现自己的组件库。如果一定程度上帮助到了你,那么维护这个项目也就有所意义。
提示
VUX 是库而非框架,虽然有专用的 vux-loader,但并不影响你自由地使用其他组件库或者工具库。
VUX 使用的 CSS 预处理工具是 less(同 WeUI),但(利益于 .vue 单文件组件的灵活性)这并不影响你使用 SASS 等其他预处理器。
用以表示该组件库时请使用大写名字 VUX,用在说明版本号时使用小写 [email protected]。
在使用VUX之前
如果你刚从后端转到前端,可能会被目前前端(表面的)工程复杂度惊吓到,但是放心,使用
vue-cli
从模板创建项目可以快速开始编码、构建, 仅仅是几行简单的命令不是么?
在使用VUX之前需要你至少已经会使用Vue
,同时需要你大概了解Node.js
,npm
,cnpm
,yarn
这些东西。
建议
Node.js
版本在7.6.0
以上。
相关工具
WeUI
VUX样式基于WeUI
,但是你不必通过使用VUX来使用WeUI
。
简单的页面你可以直接引入WeUI
样式。详细请参考WeUI 文档
。
Vue
VUX基于Vue
的组件库,意味着你需要有Vue
的基础知识。
如果还没有了解过,建议先看看Vue官方文档。
Webpack
如果你直接使用vux2
模板,你可以暂时不用了解。当你需要自定义一些配置时自然就能很快了解了。
vue-cli
Vue 官方用于快速创建项目的工具。
npm install vue-cli -g
或者使用 yarn
yarn global add vue-cli
vue-loader
webpack loader,用于编译.vue
文件,官方模板已经帮你配置好。
vux-loader
VUX组件库的webpack loader,实现按需加载等功能。它不是替代vue-loader
而是配合vue-loader
使用。
如果你使用vux2模板,暂不需要手动使用它。
2 安装和使用
2.1 安装
如果你从没使用过 VUX,请参考 快速入门。
不推荐使用 umd 方式引用组件,但是如果不得不使用,可以参考 umd 构建
直接安装或者更新:
npm install vux --save
或者使用yarn
yarn add vux // 安装
yarn upgrade vux // 更新
如果你想直接从Github安装,请指定 v2
分支
npm install git://github.com/airyland/vux.git#v2
如果你是从0.x
更新,请参考: 更新到2.x
vux2必须配合vux-loader使用, 请在build/webpack.base.conf.js里参照如下代码进行配置:
const vuxLoader = require('vux-loader')
const webpackConfig = originalConfig // 原来的 module.exports 代码赋值给变量 webpackConfig
module.exports = vuxLoader.merge(webpackConfig, {
plugins: ['vux-ui']
})
[email protected] 已经停止维护,请尽快迁移到 [email protected] & [email protected] & [email protected],虽然要花点时间,但是完全值得。
2.2 快速开始
[email protected] 推荐webpack+vue-loader方式的开发,如果要使用umd文件,请参照文档。 不建议使用引入script的方式进行开发,因为它会带来一系列的开发、维护、效率、部署问题。
Life is short, use webpack.
vux2 模板
vux2 模板 fork 自 webpack 模板,基本和官方同步。
默认为 webpack2 模板
npm install vue-cli -g # 如果还没安装
vue init airyland/vux2 projectPath
cd projectPath
npm install --registry=https://registry.npm.taobao.org # 或者 cnpm install 或者 yarn
npm run dev # 或者 yarn dev
使用淘宝 npm 镜像
cnpm
你可以直接使用 cnpm 来加速模块下载。
yarn
或者如果你已经用上了 yarn,建议配置淘宝源:
yarn config set registry https://registry.npm.taobao.org
yarn
2.3 手动配置使用
注意的是下面事项并非表示 VUX 使用繁琐,部分只是出于确保有正确的依赖和配置,而部分是出于优化。
请将
babel-loader
的配置写到.babelrc
里而不是使用options
,否则可能会出错。
折腾能力强的同学参考一下,下面即airyland/vux2
模板主要处理的事项:
- 引入
reset.less
,默认样式不包含reset,并且部分用户自己有一套reset样式,因此需要在App.vue
进行手动引入
<style lang="less">
@import '~vux/src/styles/reset.less';
</style>
- 配置
vue-loader
通过配置vux-loader实现)
// vux-loader
plugins: [{
name: 'vux-ui'
}]
- 配置
babel-loader
以正确编译 VUX 的js源码(通过配置vux-loader实现)
plugins: [{
name: 'vux-ui'
}]
- 安装
less-loader
以正确编译less源码
npm install less less-loader --save-dev
- 安装
yaml-loader
以正确进行语言文件读取
npm install yaml-loader --save-dev
- 添加
viewport
meta
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
- 添加
Fastclick
移除移动端点击延迟
const FastClick = require('fastclick')
FastClick.attach(document.body)
- 添加
vue-router
(如果不需要前端路由,可忽略)
import VueRouter from 'vue-router'
Vue.use(VueRouter)
- 添加 webpack plugin, 在构建后去除重复css代码(通过配置vux-loader实现)
plugins: [{
name: 'duplicate-style'
}]
- 如果你使用
webpack-simple
模板或者 webpack 配置里缺少 .vue extension 配置,请记得配置:
resolve: {
extensions: ['.js', '.vue', '.json']
2.4 代码示例
- .vue文件中调用组件
<template>
<div>
<group>
<cell title="title" value="value"></cell>
</group>
</div>
</template>
<script>
import { Group, Cell } from 'vux'
export default {
components: {
Group,
Cell
}
}
</script>
- main.js中调用plugin
import { AlertPlugin, ToastPlugin } from 'vux'
Vue.use(AlertPlugin)
Vue.use(ToastPlugin)
// 详细使用请参考对应组件文档
3 定制
3.1 主题颜色配置
配置插件
暂时只支持配合
vux-loader
使用。注意的是主题文件不能引入其他less文件,只能为简单变量列表。
请配置vux-loader的less-theme
插件,指定用以覆盖的less文件路径:
{
name: 'less-theme',
path: 'src/styles/theme.less' // 相对项目根目录路径
}
可配置颜色
源码地址:https://github.com/airyland/vux/blob/v2/src/styles/variable.less
更多配置需求请通过 issue 提出。
demo站点的示例配置
源代码地址:https://github.com/airyland/vux/blob/v2/src/theme.less
内部如何实现的?
vux-loader
在每个less
文件的编译过程中重写了less-loader
的变量参数,使其能直接覆盖原来变量。
4 开发
4.1 路由
4.2 在Nuxt.js中使用
4.3 TypeScript 支持
4.4 Ajax 请求
4.5 点击延迟
4.6 使用微信 jssdk
4.7 添加谷歌统计
4.8 页面切换显示 Loading
4.9 异步加载组件
4.10 区分测试环境和生产环境
4.11 全局公共函数
4.12 autoprefix 配置
4.13 禁用 eslint
5 vux-loader
5.1 vux-loader 是什么?
5.2 安装使用
5.3 插件列表
6 最佳实践
6.1 VUX tabber切换图标及字体颜色
通过path判断当前页面,并切换选中tabbar,如果tabbar不多,使用组件一般方式即可(即在每个子模块引入tabbar并写好默认选中项)
/src/App.vue
<!-- 入口文件 -->
<template>
<div id="app">
<!-- 视图层 -->
<router-view></router-view>
<div class="footer-box" v-if="$route.path === '/events' || $route.path === '/lending' || $route.path === '/user'">
<!-- 底部选项卡 -->
<tabbar class="footer-fixed">
<tabbar-item link="/lending" :selected="$route.path === '/lending'">
<img slot="icon" src="./assets/images/foot-tz.png">
<img slot="icon-active" src="./assets/images/foot-tz-active.png">
<span slot="label">首页</span>
</tabbar-item>
<tabbar-item link="/events" :selected="$route.path === '/events'">
<img slot="icon" src="./assets/images/foot-xx.png">
<img slot="icon-active" src="./assets/images/foot-xx-active.png">
<span slot="label">活动</span>
</tabbar-item>
<tabbar-item link="/user" :selected="$route.path === '/user'">
<img slot="icon" src="./assets/images/foot-wd.png">
<img slot="icon-active" src="./assets/images/foot-wd-active.png">
<span slot="label">我的</span>
</tabbar-item>
</tabbar>
</div>
</div>
</template>
<script>
// 引入 vux tabbar 组件
import { Tabbar, TabbarItem, XHeader } from 'vux'
export default {
name: 'app',
components: {
Tabbar,
TabbarItem,
XHeader
}
}
</script>
<style src="./assets/style/main.less"></style>
<style lang="less">
@import '~vux/src/styles/reset.less';
</style>
6.2 x-header、tabbar固定位置
<template>
<div id="app">
<router-view></router-view>
<div class="footer-box" v-if="$route.path === '/events' || $route.path === '/lending' || $route.path === '/user'">
<tabbar class="footer-fixed">
<tabbar-item link="/lending" :selected="$route.path === '/lending'">
<img slot="icon" src="./assets/images/foot-tz.png">
<img slot="icon-active" src="./assets/images/foot-tz-active.png">
<span slot="label">首页</span>
</tabbar-item>
<tabbar-item link="/events" :selected="$route.path === '/events'">
<img slot="icon" src="./assets/images/foot-xx.png">
<img slot="icon-active" src="./assets/images/foot-xx-active.png">
<span slot="label">活动</span>
</tabbar-item>
<tabbar-item link="/user" :selected="$route.path === '/user'">
<img slot="icon" src="./assets/images/foot-wd.png">
<img slot="icon-active" src="./assets/images/foot-wd-active.png">
<span slot="label">我的</span>
</tabbar-item>
</tabbar>
</div>
</div>
</template>
<script>
import { Tabbar, TabbarItem, XHeader } from 'vux'
export default {
name: 'app',
components: {
Tabbar,
TabbarItem,
XHeader
}
}
</script>
<style src="./assets/style/main.less"></style>
<style lang="less">
@import '~vux/src/styles/reset.less';
</style>
此处组件fix失效是因为在app.vue引用tabbar导致的,若在子页面分别引用不会影响固定效果
处理方案:
添加class 固定于页面底部
../src/assets/style/main.less
.header-fixed{
position: fixed;
top: 0;
z-index: 500;
width: 100%;
}
#app .footer-fixed{
position: fixed;
bottom: 0;
}
.header-box{
padding-top: 46px;
}
.footer-box{
padding-bottom: 53px;
}
6.3 下拉加载更多
先上效果图
- 创建项目
使用vue-cli 创建一个vue项目
安装vux,可以参考:vux快速入门
- 配置
官方文档中声明,该组件已经不再维护,也不建议使用,大部分情况下也不需要用到该组件。 建议使用第三方相关组件,相关 issue 将不会处理。不知道作者为啥不维护了,明明需求挺多的
我没有用demo里的 LoadMore 组件,用的是 Scroller里自带的 use-pullup, use-pulldown 下面是我的配置
代码示例:
<template>
<div>
<scroller use-pullup :pullup-config="pullupDefaultConfig" @on-pullup-loading="loadMore"
use-pulldown :pulldown-config="pulldownDefaultConfig" @on-pulldown-loading="refresh"
lock-x ref="scrollerBottom" height="-48">
<div>
<group label-width="4.5em" label-margin-right="2em" label-align="right" v-for="item in list" :key="item.key">
<panel :list="item.panel" :type="item.type" @on-img-error="onImgError" @click.native="goto(item)"></panel>
<x-progress :percent="item.progress" :show-cancel="false"></x-progress>
</group>
</div>
</scroller>
</div>
</template>
<script>
import axios from 'axios'
import _ from 'lodash'
import { Group, Panel, XProgress, Scroller } from 'vux'
const pulldownDefaultConfig = {
content: '下拉刷新',
height: 40,
autoRefresh: true,
downContent: '下拉刷新',
upContent: '释放后刷新',
loadingContent: '正在刷新...',
clsPrefix: 'xs-plugin-pulldown-'
}
const pullupDefaultConfig = {
content: '上拉加载更多',
pullUpHeight: 60,
height: 40,
autoRefresh: false,
downContent: '释放后加载',
upContent: '上拉加载更多',
loadingContent: '加载中...',
clsPrefix: 'xs-plugin-pullup-'
}
export default {
name: 'Lending',
components: {
Group,
Panel,
XProgress,
Scroller
},
data () {
return {
list: [],
curPage: 1,
swiper_index: 1,
pullupDefaultConfig: pullupDefaultConfig,
pulldownDefaultConfig: pulldownDefaultConfig
}
},
methods: {
swiper_onIndexChange (index) {
this.swiper_index = index
},
onImgError (item, $event) {
console.log(item, $event)
},
/**
* 刷新页面
*/
refresh () {
console.log('refresh')
},
/**
* 加载更多列表
*/
loadMore () {
var self = this
console.log('加载更多')
self.$refs.scrollerBottom.donePullup()
self.curPage++
self.getList()
},
/**
* 获取列表
*/
getList () {
var self = this
axios.get(process.env.BASE_API + '/financeJson.do', {params: { 'curPage': self.curPage }})
.then(function (res) {
_.each(res.data, function (v, k) {
var item = {
data: v,
key: v.id,
type: '4',
progress: parseInt(v.progress),
panel: [
{
title: v.borrowTitle,
desc: '融资额度:' + v.borrowAmount + ' 期限:' + v.deadline,
meta: {
source: '年利率',
date: '7% + ' + (parseInt(v.annualRate) - 7) + '%',
other: '完成比例: ' + parseInt(v.progress) + '%'
}
}
]
}
self.list.push(item)
})
})
.catch(function (error) {
console.log(error)
})
},
goto (item) {
var self = this
self.$router.push({name: 'financeDetail', params: {data: item}})
}
},
created () {
var self = this
self.getList()
}
}
</script>
6.4 scroller下拉失败回弹
引用vux中的scroller插件注意事项: 1、scroller标签内部必须紧套一层div标签 2、注意scroller的enabled属性,表示可以下拉刷新
6.5 Vue下路由History模式打包后页面空白
vue的路由在默认的hash模式下,默认打包一般不会有什么问题,不过hash模式由于url会带有一个#,不美观,而且在微信分享, 授权登录等都会有一些坑.所以history模式也会有一些应用场景.新手往往会碰到history模式打包后页面一片空白的情况, 而且没有资源加载错误的报错信息.这个其实仔细研究下会发现,如果项目直接放的跟目录, 那么是没有问题的,如果是子目录, 那么就会一片空白了.这个vue官方有解释,需要加一个base
// base: '/history',
// mode: 'history',
这个base即为项目路径.以上两个都解决了,还是会发现,此时只有首页能访问,通过首页点进去其他路由也是可以的, 但是如果在其他路由刷新就有错误了,这个懂history模式的也应该知道,history模式是h5 api的 history.pushState, 相对于是浏览器模拟了一条历史,而真正服务器上没有这个路径资源,为什么hash模式不存在这个问题呢?hash模式是带#, 这个服务器不会解析,相对于还是返回html而已,index.html会根据vue路由去解析,而history模式则会请求服务器上的此地址, 服务器上没有相关路径就会报错了,vue-router的官方文档有介绍各种配置,比如ngnix的配置
location / {
try_files $uri $uri/ /index.html;
}
上面这个对于直接项目的根目录是可以的,但是如果项目不是根目录还是会有问题,
location /history {
root C:/web/static;
index index.html index.htm;
#error_page 404 /history/index.html;
if (!-e $request_filename) {
rewrite ^/(.*) /history/index.html last;
break;
}
}
上面这个是项目路径名为history,这样配置后就不会有vue打包后页面空白问题了,history路由也可以自由访问了, 不过要记得上面说的,非根目录的项目需要加上base 的路径
6.6 打包后css引用图片资源找不到
使用vue打包,通过css引用图片资源。
.img {
height: 500px;
width: 100%;
background: url("./assets/img/1.jpg") no-repeat;
background-size: 100%;
}
热更新开发环境的效果是这样
但打完包出来的页面却报找不到资源的错误。
查了一下原因,css引入图片再打包后,style-loader无法设置自己的publicPath,于是我改变了ExtractTextPlugin的css路径publicPath。
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
// css 引用图片打包问题
publicPath: '../../../',
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
在build一次,没有报错,正常显示!
6.7 打包后js引用图片资源找不到
在vue组件的js部分导入图片要使用require的形式导入,否则webpack不能正常导入,因为其路径不符合其解析规范
原因:
- 在引入图片的时候的地址数据的时候需要使用require来讲图片作为模块引入,这样才会被webpack正确的打包的项目文件中,否则会因为webpack根据依赖打包而找不到指定的图片
- 如果是在HTML中的img中通过v-bind引入引入图片,src为对应的数据变量的话也是需要使用require来将图片作为模块引入才会被webpack正常打包
- 在HTML中直接书写的img地址以及css中引入的图片地址无需使用其他手段正常引用即可被webpack正常打包
6.8 vux框架组件自定义样式
6.8.1 全局方式
方法一 在webpack.base.conf.js文件中配置
{
name: 'less-theme',
path: 'src/styles/theme.less' // 相对项目根目录路径
}
然后在配置的路径写入对应的.less文件,类似下图这样
6.8.2 局部方式
方法二 使用/deep/或>>>
在引用的组件包一个div,例如类名为sample
要改变里面组件样式可以用在<style>
标签中用.sample /deep/ xxxxx
组件样式类来选择
注意:/deep/在less和sass中不支持,本人在使用>>>测试的时候没有生效
6.9 vux-cell title 插槽使用
<group>
<cell title="消息中心">
<span slot="default"><span style="vertical-align:middle;"></span> <badge text="99"></badge></span>
</cell>
<cell>
<span slot="title" class="edit-left">手机号码</span>
<span slot="default">{{phone}}</span>
</cell>
<cell title="第三方登录授权" >
<span slot="default"><img class="icon" slot="icon" src="../../assets/wx_icon.png"></span>
</cell>
<cell title="登录密码">
<span slot="default"></span>
</cell>
</group>
- 注意:样式被修改,可能是公共样式或其他模块样式在打包后被修改
vux cell title插槽可添加样式并使得超长文字隐藏。
可参考/yumaomoney_WeChat/src/components/user/message/Message.vue .cell-overflow
6.10 报错处理:Failed to load resource: net::ERR_FILE_NOT_FOUND
Failed to load resource: net::ERR_FILE_NOT_FOUND或者vue dist文件下的index.html没显示
vue-cli npm run dev 可以看到,但是通过dist文件下的index.html直接打开没显示,没看到
知乎上:webpack.prod.conf.js 中output添加参数publicPath:'./'
具体:
在webpack.base.conf.js里
publicPath: process.env.NODE_ENV === 'production'
? './' +config.build.assetsPublicPath
: './' + config.dev.assetsPublicPath
vue的图片路径,和背景图片路径打包后错误解决
- 找到 config->index.js里面,如下修改
- 找到 build->utils.js,在里面加入一句publicPath:’../../’
配置修改完成,接下来,使用有两种方式,这里一般和文件结构有关,下面是我的文件结构下的使用
1、图片资源放在 assets->img文件夹下面
img标签引入图片
<img src="../assets/img/loginback.png" class="test-img" />
css使用图片
background: url('../assets/img/loginback.png') no-repeat top left ;
2、图片资源放在static->img文件夹下面
img标签引入图片
<img src="../../static/img/loginback.png" class="test-img" /><br><img src="static/img/loginback.png" class="test-img" />
css使用图片
background: url('../../static/img/loginback.png') no-repeat top left ;
6.11 警告处理:warning:component lists rendered with v-for should have explicit keys
命令行warning(Emitted value instead of an instance of Error)。 component lists rendered with v-for should have explicit keys。 See https://vuejs.org/guide/list.html#key for more info.
截图如下:
这里只是推荐使用key. 原本的代码如下:
<el-tag
v-for = "feiLei of ruleForm.fenLeis"
:closable = "true"
type = "primary"
@close = "handleCloseFenLei(feiLei)">{{feiLei.name}}
</el-tag>
运行时显示warning,添加:key即可,如下:
<el-tag
v-for = "feiLei of ruleForm.fenLeis"
:key="feiLei1"
:closable = "true"
type = "primary"
@close = "handleCloseFenLei(feiLei)">{{feiLei.name}}
</el-tag>
这样就不会报错啦,具体看文档,key不是必须的,仅仅是warning
6.12 报错处理:exports is not defined
在引入插件后,控制台报错Uncaught ReferenceError: exports is not defined
处理方法:
- 引入插件需重新编译,重新
npm run dev
- webpack 2后不允许混合使用import和module.exports
- 统一修改为
export default XXX
- 找到
.babelrcf
删除transform-runtime
- 统一修改为
- 去掉 { "modules": false }其中{ "modules": false }阻止了babel进行模块转换,具体见modules配置的说明。
- 将modules改为默认设置即可
- 删除该配置
6.13 报错处理:Default export is not declared in imported module
升级 webstorm 到 2016.1 即可解决,以前的版本有这个规则但是没选项关闭
可参考yumaomoney_WeChat/src/components/container/Container.vue,export default为必要内容。
7 同类型技术比较
参考资料
- 常见问题及处理
- 报错的处理
- 插件的使用